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
|
// 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) {
for (set<TagSubgroup*>::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;
};
};
}
|