1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
// rs_config.h
// repl set configuration
//
/**
* Copyright (C) 2008 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../../util/net/hostandport.h"
#include "../../util/concurrency/race.h"
#include "health.h"
namespace mongo {
class Member;
const string rsConfigNs = "local.system.replset";
class ReplSetConfig {
enum { EMPTYCONFIG = -2 };
struct TagSubgroup;
public:
/**
* This contacts the given host and tries to get a config from them.
*
* This sends a test heartbeat to the host and, if all goes well and the
* host has a more recent config, fetches the config and loads it (see
* from().
*
* If it's contacting itself, it skips the heartbeat (for obvious
* reasons.) If something is misconfigured, throws an exception. If the
* host couldn't be queried or is just blank, ok() will be false.
*/
ReplSetConfig(const HostAndPort& h);
ReplSetConfig(BSONObj cfg, bool force=false);
bool ok() const { return _ok; }
struct TagRule;
struct MemberCfg {
MemberCfg() : _id(-1), votes(1), priority(1.0), arbiterOnly(false), slaveDelay(0), hidden(false), buildIndexes(true) { }
int _id; /* ordinal */
unsigned votes; /* how many votes this node gets. default 1. */
HostAndPort h;
double priority; /* 0 means can never be primary */
bool arbiterOnly;
int slaveDelay; /* seconds. int rather than unsigned for convenient to/front bson conversion. */
bool hidden; /* if set, don't advertise to drives in isMaster. for non-primaries (priority 0) */
bool buildIndexes; /* if false, do not create any non-_id indexes */
map<string,string> tags; /* tagging for data center, rack, etc. */
private:
set<TagSubgroup*> _groups; // the subgroups this member belongs to
public:
const set<TagSubgroup*>& groups() const {
return _groups;
}
set<TagSubgroup*>& groupsw() {
return _groups;
}
void check() const; /* check validity, assert if not. */
BSONObj asBson() const;
bool potentiallyHot() const { return !arbiterOnly && priority > 0; }
void updateGroups(const OpTime& last) {
RACECHECK
for (set<TagSubgroup*>::const_iterator it = groups().begin(); it != groups().end(); it++) {
((TagSubgroup*)(*it))->updateLast(last);
}
}
bool operator==(const MemberCfg& r) const {
if (!tags.empty() || !r.tags.empty()) {
if (tags.size() != r.tags.size()) {
return false;
}
// if they are the same size and not equal, at least one
// element in A must be different in B
for (map<string,string>::const_iterator lit = tags.begin(); lit != tags.end(); lit++) {
map<string,string>::const_iterator rit = r.tags.find((*lit).first);
if (rit == r.tags.end() || (*lit).second != (*rit).second) {
return false;
}
}
}
return _id==r._id && votes == r.votes && h == r.h && priority == r.priority &&
arbiterOnly == r.arbiterOnly && slaveDelay == r.slaveDelay && hidden == r.hidden &&
buildIndexes == buildIndexes;
}
bool operator!=(const MemberCfg& r) const { return !(*this == r); }
};
vector<MemberCfg> members;
string _id;
int version;
HealthOptions ho;
string md5;
BSONObj getLastErrorDefaults;
map<string,TagRule*> rules;
list<HostAndPort> otherMemberHostnames() const; // except self
/** @return true if could connect, and there is no cfg object there at all */
bool empty() const { return version == EMPTYCONFIG; }
string toString() const { return asBson().toString(); }
/** validate the settings. does not call check() on each member, you have to do that separately. */
void checkRsConfig() const;
/** check if modification makes sense */
static bool legalChange(const ReplSetConfig& old, const ReplSetConfig& n, string& errmsg);
//static void receivedNewConfig(BSONObj);
void saveConfigLocally(BSONObj comment); // to local db
string saveConfigEverywhere(); // returns textual info on what happened
/**
* Update members' groups when the config changes but members stay the same.
*/
void updateMembers(List1<Member> &dest);
BSONObj asBson() const;
/**
* Getter and setter for _majority. This is almost always
* members.size()/2+1, but can be the number of non-arbiter members if
* there are more arbiters than non-arbiters (writing to 3 out of 7
* servers is safe if 4 of the servers are arbiters).
*/
void setMajority();
int getMajority() const;
bool _constructed;
private:
bool _ok;
int _majority;
void from(BSONObj);
void clear();
struct TagClause;
/**
* This is a logical grouping of servers. It is pointed to by a set of
* servers with a certain tag.
*
* For example, suppose servers A, B, and C have the tag "dc" : "nyc". If we
* have a rule {"dc" : 2}, then we want A _or_ B _or_ C to have the
* write for one of the "dc" critiria to be fulfilled, so all three will
* point to this subgroup. When one of their oplog-tailing cursors is
* updated, this subgroup is updated.
*/
struct TagSubgroup : boost::noncopyable {
~TagSubgroup(); // never called; not defined
TagSubgroup(string nm) : name(nm) { }
const string name;
OpTime last;
vector<TagClause*> clauses;
// this probably won't actually point to valid members after the
// subgroup is created, as initFromConfig() makes a copy of the
// config
set<MemberCfg*> m;
void updateLast(const OpTime& op);
//string toString() const;
/**
* If two tags have the same name, they should compare as equal so
* that members don't have to update two identical groups on writes.
*/
bool operator() (TagSubgroup& lhs, TagSubgroup& rhs) const {
return lhs.name < rhs.name;
}
};
/**
* An argument in a rule. For example, if we had the rule {dc : 2,
* machines : 3}, "dc" : 2 and "machines" : 3 would be two TagClauses.
*
* Each tag clause has a set of associated subgroups. For example, if
* we had "dc" : 2, our subgroups might be "nyc", "sf", and "hk".
*/
struct TagClause {
OpTime last;
map<string,TagSubgroup*> subgroups;
TagRule *rule;
string name;
/**
* If we have get a clause like {machines : 3} and this server is
* tagged with "machines", then it's really {machines : 2}, as we
* will always be up-to-date. So, target would be 3 and
* actualTarget would be 2, in that example.
*/
int target;
int actualTarget;
void updateLast(const OpTime& op);
string toString() const;
};
/**
* Parses getLastErrorModes.
*/
void parseRules(const BSONObj& modes);
/**
* Create a hash containing every possible clause that could be used in a
* rule and the servers related to that clause.
*
* For example, suppose we have the following servers:
* A {"dc" : "ny", "ny" : "rk1"}
* B {"dc" : "ny", "ny" : "rk1"}
* C {"dc" : "ny", "ny" : "rk2"}
* D {"dc" : "sf", "sf" : "rk1"}
* E {"dc" : "sf", "sf" : "rk2"}
*
* This would give us the possible criteria:
* "dc" -> {A, B, C},{D, E}
* "ny" -> {A, B},{C}
* "sf" -> {D},{E}
*/
void _populateTagMap(map<string,TagClause> &tagMap);
public:
struct TagRule {
vector<TagClause*> clauses;
OpTime last;
void updateLast(const OpTime& op);
string toString() const;
};
};
}
|