~ubuntu-branches/ubuntu/trusty/juju-core/trusty-proposed

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/replicaset/replicaset.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-29 11:40:20 UTC
  • mfrom: (23.1.1 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20140129114020-ejieitm8smtt5vln
Tags: 1.17.1-0ubuntu2
d/tests/local-provider: Don't fail tests if ~/.juju is present as its
created by the juju version command. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package replicaset
 
2
 
 
3
import (
 
4
        "fmt"
 
5
        "io"
 
6
        "time"
 
7
 
 
8
        "labix.org/v2/mgo"
 
9
        "labix.org/v2/mgo/bson"
 
10
)
 
11
 
 
12
// Initiate sets up a replica set with the given replica set name with the
 
13
// single given member.  It need be called only once for a given mongo replica
 
14
// set.
 
15
//
 
16
// Note that you must set DialWithInfo and set Direct = true when dialing into a
 
17
// specific non-initiated mongo server.  The session will be set to Monotonic
 
18
// mode.
 
19
//
 
20
// See http://docs.mongodb.org/manual/reference/method/rs.initiate/ for more
 
21
// details.
 
22
func Initiate(session *mgo.Session, address, name string) error {
 
23
        session.SetMode(mgo.Monotonic, true)
 
24
        cfg := Config{
 
25
                Name:    name,
 
26
                Version: 1,
 
27
                Members: []Member{{Id: 1, Address: address}},
 
28
        }
 
29
        return session.Run(bson.D{{"replSetInitiate", cfg}}, nil)
 
30
}
 
31
 
 
32
// Member holds configuration information for a replica set member.
 
33
//
 
34
// See http://docs.mongodb.org/manual/reference/replica-configuration/
 
35
// for more details
 
36
type Member struct {
 
37
        // Id is a unique id for a member in a set.
 
38
        Id int `bson:"_id"`
 
39
 
 
40
        // Address holds the network address of the member,
 
41
        // in the form hostname:port.
 
42
        Address string `bson:"host"`
 
43
 
 
44
        // Arbiter holds whether the member is an arbiter only.
 
45
        // This value is optional; it defaults to false.
 
46
        Arbiter *bool `bson:"arbiterOnly,omitempty"`
 
47
 
 
48
        // BuildIndexes determines whether the mongod builds indexes on this member.
 
49
        // This value is optional; it defaults to true.
 
50
        BuildIndexes *bool `bson:"buildIndexes,omitempty"`
 
51
 
 
52
        // Hidden determines whether the replica set hides this member from
 
53
        // the output of IsMaster.
 
54
        // This value is optional; it defaults to false.
 
55
        Hidden *bool `bson:"hidden,omitempty"`
 
56
 
 
57
        // Priority determines eligibility of a member to become primary.
 
58
        // This value is optional; it defaults to 1.
 
59
        Priority *float64 `bson:"priority,omitempty"`
 
60
 
 
61
        // Tags store additional information about a replica member, often used for
 
62
        // customizing read preferences and write concern.
 
63
        Tags map[string]string `bson:"tags,omitempty"`
 
64
 
 
65
        // SlaveDelay describes the number of seconds behind the master that this
 
66
        // replica set member should lag rounded up to the nearest second.
 
67
        // This value is optional; it defaults to 0.
 
68
        SlaveDelay *time.Duration `bson:"slaveDelay,omitempty"`
 
69
 
 
70
        // Votes controls the number of votes a server has in a replica set election.
 
71
        // This value is optional; it defaults to 1.
 
72
        Votes *int `bson:"votes,omitempty"`
 
73
}
 
74
 
 
75
// Add adds the given members to the session's replica set.  Duplicates of
 
76
// existing replicas will be ignored.
 
77
//
 
78
// Members will have their Ids set automatically if they are not already > 0
 
79
func Add(session *mgo.Session, members ...Member) error {
 
80
        config, err := CurrentConfig(session)
 
81
        if err != nil {
 
82
                return err
 
83
        }
 
84
 
 
85
        config.Version++
 
86
        max := 0
 
87
        for _, member := range config.Members {
 
88
                if member.Id > max {
 
89
                        max = member.Id
 
90
                }
 
91
        }
 
92
 
 
93
outerLoop:
 
94
        for _, newMember := range members {
 
95
                for _, member := range config.Members {
 
96
                        if member.Address == newMember.Address {
 
97
                                // already exists, skip it
 
98
                                continue outerLoop
 
99
                        }
 
100
                }
 
101
                // let the caller specify an id if they want, treat zero as unspecified
 
102
                if newMember.Id < 1 {
 
103
                        max++
 
104
                        newMember.Id = max
 
105
                }
 
106
                config.Members = append(config.Members, newMember)
 
107
        }
 
108
        return session.Run(bson.D{{"replSetReconfig", config}}, nil)
 
109
}
 
110
 
 
111
// Remove removes members with the given addresses from the replica set. It is
 
112
// not an error to remove addresses of non-existent replica set members.
 
113
func Remove(session *mgo.Session, addrs ...string) error {
 
114
        config, err := CurrentConfig(session)
 
115
        if err != nil {
 
116
                return err
 
117
        }
 
118
        config.Version++
 
119
        for _, rem := range addrs {
 
120
                for n, repl := range config.Members {
 
121
                        if repl.Address == rem {
 
122
                                config.Members = append(config.Members[:n], config.Members[n+1:]...)
 
123
                                break
 
124
                        }
 
125
                }
 
126
        }
 
127
        err = session.Run(bson.D{{"replSetReconfig", config}}, nil)
 
128
        if err == io.EOF {
 
129
                // EOF means we got disconnected due to the Remove... this is normal.
 
130
                // Refreshing should fix us up.
 
131
                session.Refresh()
 
132
                err = nil
 
133
        }
 
134
        return err
 
135
}
 
136
 
 
137
// Set changes the current set of replica set members.  Members will have their
 
138
// ids set automatically if their ids are not already > 0.
 
139
func Set(session *mgo.Session, members []Member) error {
 
140
        config, err := CurrentConfig(session)
 
141
        if err != nil {
 
142
                return err
 
143
        }
 
144
 
 
145
        config.Version++
 
146
 
 
147
        // Assign ids to members that did not previously exist, starting above the
 
148
        // value of the highest id that already existed
 
149
        ids := map[string]int{}
 
150
        max := 0
 
151
        for _, m := range config.Members {
 
152
                ids[m.Address] = m.Id
 
153
                if m.Id > max {
 
154
                        max = m.Id
 
155
                }
 
156
        }
 
157
 
 
158
        for x, m := range members {
 
159
                if id, ok := ids[m.Address]; ok {
 
160
                        m.Id = id
 
161
                } else if m.Id < 1 {
 
162
                        max++
 
163
                        m.Id = max
 
164
                }
 
165
                members[x] = m
 
166
        }
 
167
 
 
168
        config.Members = members
 
169
 
 
170
        err = session.Run(bson.D{{"replSetReconfig", config}}, nil)
 
171
        if err == io.EOF {
 
172
                // EOF means we got disconnected due to a Remove... this is normal.
 
173
                // Refreshing should fix us up.
 
174
                session.Refresh()
 
175
                err = nil
 
176
        }
 
177
        return err
 
178
}
 
179
 
 
180
// Config reports information about the configuration of a given mongo node
 
181
type IsMasterResults struct {
 
182
        // The following fields hold information about the specific mongodb node.
 
183
        IsMaster  bool      `bson:"ismaster"`
 
184
        Secondary bool      `bson:"secondary"`
 
185
        Arbiter   bool      `bson:"arbiterOnly"`
 
186
        Address   string    `bson:"me"`
 
187
        LocalTime time.Time `bson:"localTime"`
 
188
 
 
189
        // The following fields hold information about the replica set.
 
190
        ReplicaSetName string   `bson:"setName"`
 
191
        Addresses      []string `bson:"hosts"`
 
192
        Arbiters       []string `bson:"arbiters"`
 
193
        PrimaryAddress string   `bson:"primary"`
 
194
}
 
195
 
 
196
// IsMaster returns information about the configuration of the node that
 
197
// the given session is connected to.
 
198
func IsMaster(session *mgo.Session) (*IsMasterResults, error) {
 
199
        results := &IsMasterResults{}
 
200
        err := session.Run("isMaster", results)
 
201
        if err != nil {
 
202
                return nil, err
 
203
        }
 
204
        return results, nil
 
205
}
 
206
 
 
207
// CurrentMembers returns the current members of the replica set.
 
208
func CurrentMembers(session *mgo.Session) ([]Member, error) {
 
209
        cfg, err := CurrentConfig(session)
 
210
        if err != nil {
 
211
                return nil, err
 
212
        }
 
213
        return cfg.Members, nil
 
214
}
 
215
 
 
216
// CurrentConfig returns the Config for the given session's replica set.
 
217
func CurrentConfig(session *mgo.Session) (*Config, error) {
 
218
        cfg := &Config{}
 
219
        err := session.DB("local").C("system.replset").Find(nil).One(cfg)
 
220
        if err != nil {
 
221
                return nil, fmt.Errorf("Error getting replset config : %s", err.Error())
 
222
        }
 
223
        return cfg, nil
 
224
}
 
225
 
 
226
// Config is the document stored in mongodb that defines the servers in the
 
227
// replica set
 
228
type Config struct {
 
229
        Name    string   `bson:"_id"`
 
230
        Version int      `bson:"version"`
 
231
        Members []Member `bson:"members"`
 
232
}
 
233
 
 
234
// CurrentStatus returns the status of the replica set for the given session.
 
235
func CurrentStatus(session *mgo.Session) (*Status, error) {
 
236
        status := &Status{}
 
237
        err := session.Run("replSetGetStatus", status)
 
238
        if err != nil {
 
239
                return nil, fmt.Errorf("Error from replSetGetStatus: %v", err)
 
240
        }
 
241
        return status, nil
 
242
}
 
243
 
 
244
// Status holds data about the status of members of the replica set returned
 
245
// from replSetGetStatus
 
246
//
 
247
// See http://docs.mongodb.org/manual/reference/command/replSetGetStatus/#dbcmd.replSetGetStatus
 
248
type Status struct {
 
249
        Name    string         `bson:"set"`
 
250
        Members []MemberStatus `bson:"members"`
 
251
}
 
252
 
 
253
// Status holds the status of a replica set member returned from
 
254
// replSetGetStatus.
 
255
type MemberStatus struct {
 
256
        // Id holds the replica set id of the member that the status is describing.
 
257
        Id int `bson:"_id"`
 
258
 
 
259
        // Address holds address of the member that the status is describing.
 
260
        Address string `bson:"name"`
 
261
 
 
262
        // Self holds whether this is the status for the member that
 
263
        // the session is connected to.
 
264
        Self bool `bson:"self"`
 
265
 
 
266
        // ErrMsg holds the most recent error or status message received
 
267
        // from the member.
 
268
        ErrMsg string `bson:"errmsg"`
 
269
 
 
270
        // Healthy reports whether the member is up. It is true for the
 
271
        // member that the request was made to.
 
272
        Healthy bool `bson:"health"`
 
273
 
 
274
        // State describes the current state of the member.
 
275
        State MemberState `bson:"state"`
 
276
 
 
277
        // Uptime describes how long the member has been online.
 
278
        Uptime time.Duration `bson:"uptime"`
 
279
 
 
280
        // Ping describes the length of time a round-trip packet takes to travel
 
281
        // between the remote member and the local instance.  It is zero for the
 
282
        // member that the session is connected to.
 
283
        Ping time.Duration `bson:"pingMS"`
 
284
}
 
285
 
 
286
// MemberState represents the state of a replica set member.
 
287
// See http://docs.mongodb.org/manual/reference/replica-states/
 
288
type MemberState int
 
289
 
 
290
const (
 
291
        StartupState = iota
 
292
        PrimaryState
 
293
        SecondaryState
 
294
        RecoveringState
 
295
        FatalState
 
296
        Startup2State
 
297
        UnknownState
 
298
        ArbiterState
 
299
        DownState
 
300
        RollbackState
 
301
        ShunnedState
 
302
)
 
303
 
 
304
var memberStateStrings = []string{
 
305
        StartupState:    "STARTUP",
 
306
        PrimaryState:    "PRIMARY",
 
307
        SecondaryState:  "SECONDARY",
 
308
        RecoveringState: "RECOVERING",
 
309
        FatalState:      "FATAL",
 
310
        Startup2State:   "STARTUP2",
 
311
        UnknownState:    "UNKNOWN",
 
312
        ArbiterState:    "ARBITER",
 
313
        DownState:       "DOWN",
 
314
        RollbackState:   "ROLLBACK",
 
315
        ShunnedState:    "SHUNNED",
 
316
}
 
317
 
 
318
// String returns a string describing the state.
 
319
func (state MemberState) String() string {
 
320
        if state < 0 || int(state) >= len(memberStateStrings) {
 
321
                return "INVALID_MEMBER_STATE"
 
322
        }
 
323
        return memberStateStrings[state]
 
324
}