Files
mongo/db/repl/rs_config.cpp

665 lines
26 KiB
C++
Raw Normal View History

2010-04-20 19:07:37 -04:00
// rs_config.cpp
2010-04-20 16:24:06 -04:00
/**
* 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/>.
*/
2010-04-27 15:27:52 -04:00
#include "pch.h"
2010-05-29 15:45:47 -04:00
#include "rs.h"
2010-04-20 21:55:34 -04:00
#include "../../client/dbclient.h"
2010-05-14 14:01:51 -04:00
#include "../../client/syncclusterconnection.h"
#include "../../util/net/hostandport.h"
2010-04-28 09:54:01 -04:00
#include "../dbhelpers.h"
2010-05-09 17:29:35 -04:00
#include "connections.h"
2010-06-04 15:54:25 -04:00
#include "../oplog.h"
2011-06-21 01:56:49 -04:00
#include "../instance.h"
#include "../../util/text.h"
#include <boost/algorithm/string.hpp>
2010-04-20 16:24:06 -04:00
2010-04-24 19:56:42 -04:00
using namespace bson;
2011-01-04 00:40:41 -05:00
namespace mongo {
2010-04-20 16:24:06 -04:00
2010-06-29 18:27:09 -04:00
void logOpInitiate(const bo&);
2011-01-04 00:40:41 -05:00
void assertOnlyHas(BSONObj o, const set<string>& fields) {
BSONObj::iterator i(o);
while( i.more() ) {
BSONElement e = i.next();
if( !fields.count( e.fieldName() ) ) {
2011-08-19 12:07:53 -04:00
uasserted(13434, str::stream() << "unexpected field '" << e.fieldName() << "' in object");
}
}
}
2011-01-04 00:40:41 -05:00
list<HostAndPort> ReplSetConfig::otherMemberHostnames() const {
list<HostAndPort> L;
for( vector<MemberCfg>::const_iterator i = members.begin(); i != members.end(); i++ ) {
if( !i->h.isSelf() )
L.push_back(i->h);
}
return L;
}
2011-01-04 00:40:41 -05:00
2010-06-29 18:27:09 -04:00
/* comment MUST only be set when initiating the set by the initiator */
2011-01-04 00:40:41 -05:00
void ReplSetConfig::saveConfigLocally(bo comment) {
2010-08-26 13:47:27 -04:00
checkRsConfig();
2010-05-13 17:18:17 -04:00
log() << "replSet info saving a newer config version to local.system.replset" << rsLog;
2011-01-04 00:40:41 -05:00
{
2010-06-04 15:54:25 -04:00
writelock lk("");
Client::Context cx( rsConfigNs );
cx.db()->flushFiles(true);
//theReplSet->lastOpTimeWritten = ??;
//rather than above, do a logOp()? probably
2010-05-13 17:18:17 -04:00
BSONObj o = asBson();
Helpers::putSingletonGod(rsConfigNs.c_str(), o, false/*logOp=false; local db so would work regardless...*/);
2011-06-14 16:22:51 -04:00
if( !comment.isEmpty() && (!theReplSet || theReplSet->isPrimary()) )
2010-06-29 18:27:09 -04:00
logOpInitiate(comment);
cx.db()->flushFiles(true);
2010-05-13 17:18:17 -04:00
}
log() << "replSet saveConfigLocally done" << rsLog;
2010-04-28 08:25:56 -04:00
}
2011-01-04 00:40:41 -05:00
bo ReplSetConfig::MemberCfg::asBson() const {
2010-04-27 14:22:46 -04:00
bob b;
b << "_id" << _id;
b.append("host", h.toString());
if( votes != 1 ) b << "votes" << votes;
if( priority != 1.0 ) b << "priority" << priority;
if( arbiterOnly ) b << "arbiterOnly" << true;
2010-08-18 14:29:04 -04:00
if( slaveDelay ) b << "slaveDelay" << slaveDelay;
2010-08-18 11:37:17 -04:00
if( hidden ) b << "hidden" << hidden;
if( !buildIndexes ) b << "buildIndexes" << buildIndexes;
2011-01-04 00:40:41 -05:00
if( !tags.empty() ) {
2011-08-16 13:49:57 -04:00
BSONObjBuilder a;
for( map<string,string>::const_iterator i = tags.begin(); i != tags.end(); i++ )
a.append((*i).first, (*i).second);
b.append("tags", a.done());
2010-11-02 12:12:29 -04:00
}
2010-04-27 14:22:46 -04:00
return b.obj();
}
2011-08-16 11:52:48 -04:00
void ReplSetConfig::updateMembers(List1<Member> &dest) {
for (vector<MemberCfg>::iterator source = members.begin(); source < members.end(); source++) {
for( Member *d = dest.head(); d; d = d->next() ) {
if (d->fullName() == (*source).h.toString()) {
d->configw().groupsw() = (*source).groups();
}
}
}
}
2011-01-04 00:40:41 -05:00
bo ReplSetConfig::asBson() const {
2010-04-24 19:56:42 -04:00
bob b;
b.append("_id", _id).append("version", version);
2010-04-27 14:22:46 -04:00
BSONArrayBuilder a;
for( unsigned i = 0; i < members.size(); i++ )
a.append( members[i].asBson() );
b.append("members", a.arr());
2011-06-09 15:04:07 -04:00
if( !ho.isDefault() || !getLastErrorDefaults.isEmpty() || !rules.empty()) {
bob settings;
if( !rules.empty() ) {
bob modes;
for (map<string,TagRule*>::const_iterator it = rules.begin(); it != rules.end(); it++) {
bob clauses;
vector<TagClause*> r = (*it).second->clauses;
for (vector<TagClause*>::iterator it2 = r.begin(); it2 < r.end(); it2++) {
clauses << (*it2)->name << (*it2)->target;
}
modes << (*it).first << clauses.obj();
}
settings << "getLastErrorModes" << modes.obj();
}
if( !getLastErrorDefaults.isEmpty() )
settings << "getLastErrorDefaults" << getLastErrorDefaults;
b << "settings" << settings.obj();
}
2010-04-24 19:56:42 -04:00
return b.obj();
}
static inline void mchk(bool expr) {
uassert(13126, "bad Member config", expr);
}
2011-01-04 00:40:41 -05:00
void ReplSetConfig::MemberCfg::check() const {
2010-04-25 16:13:21 -04:00
mchk(_id >= 0 && _id <= 255);
mchk(priority >= 0 && priority <= 1000);
mchk(votes <= 100); // votes >= 0 because it is unsigned
2011-04-12 13:56:32 -04:00
uassert(13419, "priorities must be between 0.0 and 100.0", priority >= 0.0 && priority <= 100.0);
2010-08-17 23:35:53 -04:00
uassert(13437, "slaveDelay requires priority be zero", slaveDelay == 0 || priority == 0);
uassert(13438, "bad slaveDelay value", slaveDelay >= 0 && slaveDelay <= 3600 * 24 * 366);
2010-08-18 12:07:21 -04:00
uassert(13439, "priority must be 0 when hidden=true", priority == 0 || !hidden);
2010-11-11 15:37:36 -05:00
uassert(13477, "priority must be 0 when buildIndexes=false", buildIndexes || priority == 0);
}
2011-07-16 18:56:59 -04:00
/*
2011-06-09 15:04:07 -04:00
string ReplSetConfig::TagSubgroup::toString() const {
bool first = true;
string result = "\""+name+"\": [";
2011-07-16 18:56:59 -04:00
for (set<const MemberCfg*>::const_iterator i = m.begin(); i != m.end(); i++) {
2011-06-09 15:04:07 -04:00
if (!first) {
result += ", ";
}
first = false;
result += (*i)->h.toString();
}
return result+"]";
}
2011-07-16 18:56:59 -04:00
*/
2011-06-09 15:04:07 -04:00
string ReplSetConfig::TagClause::toString() const {
string result = name+": {";
for (map<string,TagSubgroup*>::const_iterator i = subgroups.begin(); i != subgroups.end(); i++) {
2011-07-16 18:56:59 -04:00
//TEMP? result += (*i).second->toString()+", ";
2011-06-09 15:04:07 -04:00
}
2011-07-16 18:56:59 -04:00
result += "TagClause toString TEMPORARILY DISABLED";
2011-06-09 15:04:07 -04:00
return result + "}";
}
string ReplSetConfig::TagRule::toString() const {
string result = "{";
for (vector<TagClause*>::const_iterator it = clauses.begin(); it < clauses.end(); it++) {
result += ((TagClause*)(*it))->toString()+",";
}
return result+"}";
}
void ReplSetConfig::TagSubgroup::updateLast(const OpTime& op) {
RACECHECK
2011-06-09 15:04:07 -04:00
if (last < op) {
last = op;
for (vector<TagClause*>::iterator it = clauses.begin(); it < clauses.end(); it++) {
(*it)->updateLast(op);
}
}
}
void ReplSetConfig::TagClause::updateLast(const OpTime& op) {
RACECHECK
2011-06-09 15:04:07 -04:00
if (last >= op) {
return;
}
// check at least n subgroups greater than clause.last
int count = 0;
map<string,TagSubgroup*>::iterator it;
for (it = subgroups.begin(); it != subgroups.end(); it++) {
if ((*it).second->last >= op) {
count++;
}
}
if (count >= actualTarget) {
last = op;
rule->updateLast(op);
}
}
void ReplSetConfig::TagRule::updateLast(const OpTime& op) {
OpTime *earliest = (OpTime*)&op;
vector<TagClause*>::iterator it;
for (it = clauses.begin(); it < clauses.end(); it++) {
if ((*it)->last < *earliest) {
earliest = &(*it)->last;
}
}
// rules are simply and-ed clauses, so whatever the most-behind
// clause is at is what the rule is at
last = *earliest;
}
/** @param o old config
2011-01-04 00:40:41 -05:00
@param n new config
*/
2011-01-04 00:40:41 -05:00
/*static*/
bool ReplSetConfig::legalChange(const ReplSetConfig& o, const ReplSetConfig& n, string& errmsg) {
assert( theReplSet );
2011-01-04 00:40:41 -05:00
if( o._id != n._id ) {
errmsg = "set name may not change";
2010-07-08 14:41:49 -04:00
return false;
}
/* TODO : wonder if we need to allow o.version < n.version only, which is more lenient.
2011-01-04 00:40:41 -05:00
if someone had some intermediate config this node doesnt have, that could be
2010-07-08 14:41:49 -04:00
necessary. but then how did we become primary? so perhaps we are fine as-is.
*/
2011-06-14 16:22:51 -04:00
if( o.version >= n.version ) {
errmsg = str::stream() << "version number must increase, old: "
<< o.version << " new: " << n.version;
2010-07-08 14:41:49 -04:00
return false;
}
map<HostAndPort,const ReplSetConfig::MemberCfg*> old;
bool isLocalHost = false;
2011-01-04 00:40:41 -05:00
for( vector<ReplSetConfig::MemberCfg>::const_iterator i = o.members.begin(); i != o.members.end(); i++ ) {
if (i->h.isLocalHost()) {
isLocalHost = true;
}
old[i->h] = &(*i);
}
int me = 0;
2011-01-04 00:40:41 -05:00
for( vector<ReplSetConfig::MemberCfg>::const_iterator i = n.members.begin(); i != n.members.end(); i++ ) {
const ReplSetConfig::MemberCfg& m = *i;
if ( (isLocalHost && !m.h.isLocalHost()) || (!isLocalHost && m.h.isLocalHost())) {
log() << "reconfig error, cannot switch between localhost and hostnames: "
<< m.h.toString() << rsLog;
uasserted(13645, "hosts cannot switch between localhost and hostname");
}
2011-01-04 00:40:41 -05:00
if( old.count(m.h) ) {
const ReplSetConfig::MemberCfg& oldCfg = *old[m.h];
2011-01-04 00:40:41 -05:00
if( oldCfg._id != m._id ) {
log() << "replSet reconfig error with member: " << m.h.toString() << rsLog;
uasserted(13432, "_id may not change for members");
}
2011-01-04 00:40:41 -05:00
if( oldCfg.buildIndexes != m.buildIndexes ) {
log() << "replSet reconfig error with member: " << m.h.toString() << rsLog;
uasserted(13476, "buildIndexes may not change for members");
}
2010-10-31 11:45:06 -04:00
/* are transitions to and from arbiterOnly guaranteed safe? if not, we should disallow here.
2010-10-31 12:11:38 -04:00
there is a test at replsets/replsetarb3.js */
2011-01-04 00:40:41 -05:00
if( oldCfg.arbiterOnly != m.arbiterOnly ) {
2010-10-30 14:56:56 -04:00
log() << "replSet reconfig error with member: " << m.h.toString() << " arbiterOnly cannot change. remove and readd the member instead " << rsLog;
uasserted(13510, "arbiterOnly may not change for members");
}
2010-10-30 14:57:49 -04:00
}
2011-01-04 00:40:41 -05:00
if( m.h.isSelf() )
me++;
}
uassert(13433, "can't find self in new replset config", me == 1);
2010-07-08 14:41:49 -04:00
/* TODO : MORE CHECKS HERE */
2010-11-12 16:09:14 -05:00
DEV log() << "replSet TODO : don't allow removal of a node until we handle it at the removed node end?" << endl;
// we could change its votes to zero perhaps instead as a short term...
2010-07-08 14:41:49 -04:00
return true;
}
2011-01-04 00:40:41 -05:00
void ReplSetConfig::clear() {
version = -5;
_ok = false;
}
void ReplSetConfig::setMajority() {
int total = members.size();
int nonArbiters = total;
int strictMajority = total/2+1;
for (vector<MemberCfg>::iterator it = members.begin(); it < members.end(); it++) {
if ((*it).arbiterOnly) {
nonArbiters--;
}
}
// majority should be all "normal" members if we have something like 4
// arbiters & 3 normal members
_majority = (strictMajority > nonArbiters) ? nonArbiters : strictMajority;
}
int ReplSetConfig::getMajority() const {
return _majority;
}
2011-01-04 00:40:41 -05:00
void ReplSetConfig::checkRsConfig() const {
2010-04-27 14:37:41 -04:00
uassert(13132,
str::stream() << "nonmatching repl set name in _id field: " << _id << " vs. " << cmdLine.ourSetName(),
2011-01-04 00:40:41 -05:00
_id == cmdLine.ourSetName());
2010-06-24 13:42:26 -04:00
uassert(13308, "replSet bad config version #", version > 0);
uassert(13133, "replSet bad config no members", members.size() >= 1);
uassert(13309, "replSet bad config maximum number of members is 12", members.size() <= 12);
{
unsigned voters = 0;
2011-01-04 00:40:41 -05:00
for( vector<MemberCfg>::const_iterator i = members.begin(); i != members.end(); ++i ) {
if( i->votes )
voters++;
}
uassert(13612, "replSet bad config maximum number of voting members is 7", voters <= 7);
uassert(13613, "replSet bad config no voting members", voters > 0);
}
2010-04-27 14:37:41 -04:00
}
2011-06-09 15:04:07 -04:00
void ReplSetConfig::_populateTagMap(map<string,TagClause> &tagMap) {
2011-08-16 13:49:57 -04:00
// create subgroups for each server corresponding to each of
// its tags. E.g.:
//
// A is tagged with {"server" : "A", "dc" : "ny"}
// B is tagged with {"server" : "B", "dc" : "ny"}
//
// At the end of this step, tagMap will contain:
//
// "server" => {"A" : [A], "B" : [B]}
// "dc" => {"ny" : [A,B]}
2011-06-09 15:04:07 -04:00
for (unsigned i=0; i<members.size(); i++) {
MemberCfg member = members[i];
2011-08-16 13:49:57 -04:00
for (map<string,string>::iterator tag = member.tags.begin(); tag != member.tags.end(); tag++) {
string label = (*tag).first;
string value = (*tag).second;
2011-06-09 15:04:07 -04:00
2011-08-16 13:49:57 -04:00
TagClause& clause = tagMap[label];
clause.name = label;
2011-06-09 15:04:07 -04:00
TagSubgroup* subgroup;
2011-08-16 13:49:57 -04:00
// search for "ny" in "dc"'s clause
if (clause.subgroups.find(value) == clause.subgroups.end()) {
clause.subgroups[value] = subgroup = new TagSubgroup(value);
2011-06-09 15:04:07 -04:00
}
else {
2011-08-16 13:49:57 -04:00
subgroup = clause.subgroups[value];
2011-06-09 15:04:07 -04:00
}
subgroup->m.insert(&members[i]);
}
}
}
void ReplSetConfig::parseRules(const BSONObj& modes) {
map<string,TagClause> tagMap;
_populateTagMap(tagMap);
for (BSONObj::iterator i = modes.begin(); i.more(); ) {
2011-06-09 15:36:34 -04:00
unsigned int primaryOnly = 0;
2011-06-09 15:04:07 -04:00
// ruleName : {dc : 2, m : 3}
BSONElement rule = i.next();
uassert(14046, "getLastErrorMode rules must be objects", rule.type() == mongo::Object);
TagRule* r = new TagRule();
BSONObj clauseObj = rule.Obj();
for (BSONObj::iterator c = clauseObj.begin(); c.more(); ) {
BSONElement clauseElem = c.next();
uassert(14829, "getLastErrorMode criteria must be numeric", clauseElem.isNumber());
// get the clause, e.g., "x.y" : 3
const char *criteria = clauseElem.fieldName();
int value = clauseElem.numberInt();
uassert(14828, str::stream() << "getLastErrorMode criteria must be greater than 0: " << clauseElem, value > 0);
TagClause* node = new TagClause(tagMap[criteria]);
int numGroups = node->subgroups.size();
uassert(14831, str::stream() << "mode " << clauseObj << " requires "
<< value << " tagged with " << criteria << ", but only "
<< numGroups << " with this tag were found", numGroups >= value);
node->name = criteria;
node->target = value;
// if any subgroups contain "me", we can decrease the target
node->actualTarget = node->target;
// then we want to add pointers between clause & subgroup
for (map<string,TagSubgroup*>::iterator sgs = node->subgroups.begin();
sgs != node->subgroups.end(); sgs++) {
bool foundMe = false;
(*sgs).second->clauses.push_back(node);
// if this subgroup contains the primary, it's automatically always up-to-date
2011-07-16 18:56:59 -04:00
for( set<MemberCfg*>::const_iterator cfg = (*sgs).second->m.begin();
cfg != (*sgs).second->m.end();
cfg++)
{
2011-06-09 15:04:07 -04:00
if ((*cfg)->h.isSelf()) {
node->actualTarget--;
foundMe = true;
}
}
2011-07-16 18:56:59 -04:00
for (set<MemberCfg *>::iterator cfg = (*sgs).second->m.begin();
2011-06-09 15:04:07 -04:00
!foundMe && cfg != (*sgs).second->m.end(); cfg++) {
2011-08-16 11:52:48 -04:00
(*cfg)->groupsw().insert((*sgs).second);
2011-06-09 15:04:07 -04:00
}
}
// if all of the members of this clause involve the primary, it's always up-to-date
if (node->actualTarget == 0) {
node->last = OpTime(INT_MAX, INT_MAX);
primaryOnly++;
}
// this is a valid clause, so we want to add it to its rule
node->rule = r;
r->clauses.push_back(node);
}
// if all of the clauses are satisfied by the primary, this rule is trivially true
if (primaryOnly == r->clauses.size()) {
r->last = OpTime(INT_MAX, INT_MAX);
}
// if we got here, this is a valid rule
2011-07-16 12:43:51 -04:00
LOG(1) << "replSet new rule " << rule.fieldName() << ": " << r->toString() << rsLog;
2011-06-09 15:04:07 -04:00
rules[rule.fieldName()] = r;
}
}
2010-04-21 16:14:28 -04:00
void ReplSetConfig::from(BSONObj o) {
2010-08-12 13:13:25 -04:00
static const string legal[] = {"_id","version", "members","settings"};
static const set<string> legals(legal, legal + 4);
assertOnlyHas(o, legals);
2010-04-21 16:43:51 -04:00
md5 = o.md5();
2010-04-21 15:26:08 -04:00
_id = o["_id"].String();
if( o["version"].ok() ) {
version = o["version"].numberInt();
2010-05-09 15:16:14 -04:00
uassert(13115, "bad " + rsConfigNs + " config: version", version > 0);
}
2010-04-21 14:41:09 -04:00
2010-04-21 16:14:28 -04:00
set<string> hosts;
2010-04-25 16:13:21 -04:00
set<int> ords;
2010-04-27 14:22:46 -04:00
vector<BSONElement> members;
try {
members = o["members"].Array();
}
catch(...) {
2010-04-27 18:55:45 -04:00
uasserted(13131, "replSet error parsing (or missing) 'members' field in config object");
2010-04-27 14:22:46 -04:00
}
unsigned localhosts = 0;
2010-04-21 16:14:28 -04:00
for( unsigned i = 0; i < members.size(); i++ ) {
BSONObj mobj = members[i].Obj();
2010-05-05 14:57:49 -04:00
MemberCfg m;
2010-04-21 14:41:09 -04:00
try {
2011-01-04 00:40:41 -05:00
static const string legal[] = {
"_id","votes","priority","host", "hidden","slaveDelay",
"arbiterOnly","buildIndexes","tags","initialSync" // deprecated
2011-01-04 00:40:41 -05:00
};
static const set<string> legals(legal, legal + 10);
assertOnlyHas(mobj, legals);
2011-01-04 00:40:41 -05:00
try {
2010-04-27 18:55:45 -04:00
m._id = (int) mobj["_id"].Number();
2011-01-04 00:40:41 -05:00
}
catch(...) {
2010-07-20 16:46:32 -04:00
/* TODO: use of string exceptions may be problematic for reconfig case! */
2011-01-04 00:40:41 -05:00
throw "_id must be numeric";
2010-07-20 16:46:32 -04:00
}
2010-04-27 18:55:45 -04:00
try {
string s = mobj["host"].String();
boost::trim(s);
2010-05-14 13:07:24 -04:00
m.h = HostAndPort(s);
2011-06-10 14:36:41 -04:00
if (!m.h.hasPort()) {
m.h.setPort(m.h.port());
}
2010-04-27 18:55:45 -04:00
}
2011-01-04 00:40:41 -05:00
catch(...) {
2010-07-22 14:20:13 -04:00
throw string("bad or missing host field? ") + mobj.toString();
2010-07-20 16:46:32 -04:00
}
2011-01-04 00:40:41 -05:00
if( m.h.isLocalHost() )
localhosts++;
m.arbiterOnly = mobj["arbiterOnly"].trueValue();
2010-08-17 23:35:53 -04:00
m.slaveDelay = mobj["slaveDelay"].numberInt();
2010-08-18 11:37:17 -04:00
if( mobj.hasElement("hidden") )
m.hidden = mobj["hidden"].trueValue();
2011-01-04 00:40:41 -05:00
if( mobj.hasElement("buildIndexes") )
m.buildIndexes = mobj["buildIndexes"].trueValue();
2010-06-29 13:17:57 -04:00
if( mobj.hasElement("priority") )
m.priority = mobj["priority"].Number();
if( mobj.hasElement("votes") )
m.votes = (unsigned) mobj["votes"].Number();
2010-11-02 12:12:29 -04:00
if( mobj.hasElement("tags") ) {
2011-08-16 13:49:57 -04:00
const BSONObj &t = mobj["tags"].Obj();
for (BSONObj::iterator c = t.begin(); c.more(); c.next()) {
m.tags[(*c).fieldName()] = (*c).String();
}
uassert(14827, "arbiters cannot have tags", !m.arbiterOnly || m.tags.empty() );
2010-11-02 12:12:29 -04:00
}
m.check();
2010-04-21 14:41:09 -04:00
}
2011-01-04 00:40:41 -05:00
catch( const char * p ) {
2010-05-07 16:42:55 -04:00
log() << "replSet cfg parsing exception for members[" << i << "] " << p << rsLog;
2010-04-27 18:55:45 -04:00
stringstream ss;
ss << "replSet members[" << i << "] " << p;
uassert(13107, ss.str(), false);
}
2011-01-04 00:40:41 -05:00
catch(DBException& e) {
2010-05-07 16:42:55 -04:00
log() << "replSet cfg parsing exception for members[" << i << "] " << e.what() << rsLog;
2010-04-27 14:22:46 -04:00
stringstream ss;
2010-08-04 10:50:53 -04:00
ss << "bad config for member[" << i << "] " << e.what();
2010-04-27 18:55:45 -04:00
uassert(13135, ss.str(), false);
2010-04-21 14:41:09 -04:00
}
2010-07-30 15:41:54 -04:00
if( !(ords.count(m._id) == 0 && hosts.count(m.h.toString()) == 0) ) {
log() << "replSet " << o.toString() << rsLog;
uassert(13108, "bad replset config -- duplicate hosts in the config object?", false);
}
2010-04-21 14:41:09 -04:00
hosts.insert(m.h.toString());
2010-04-25 16:13:21 -04:00
ords.insert(m._id);
2010-04-27 14:22:46 -04:00
this->members.push_back(m);
2010-04-21 14:41:09 -04:00
}
uassert(13393, "can't use localhost in repl set member names except when using it for all members", localhosts == 0 || localhosts == members.size());
2010-05-09 15:16:14 -04:00
uassert(13117, "bad " + rsConfigNs + " config", !_id.empty());
2011-06-09 15:04:07 -04:00
if( o["settings"].ok() ) {
BSONObj settings = o["settings"].Obj();
if( settings["getLastErrorModes"].ok() ) {
parseRules(settings["getLastErrorModes"].Obj());
}
ho.check();
try { getLastErrorDefaults = settings["getLastErrorDefaults"].Obj().copy(); }
catch(...) { }
}
// figure out the majority for this config
setMajority();
2010-04-20 21:55:34 -04:00
}
2010-04-20 16:24:06 -04:00
2010-04-21 16:43:51 -04:00
static inline void configAssert(bool expr) {
2010-07-08 14:41:49 -04:00
uassert(13122, "bad repl set config?", expr);
2010-04-21 16:43:51 -04:00
}
2011-06-14 16:22:51 -04:00
ReplSetConfig::ReplSetConfig(BSONObj cfg, bool force) {
2011-07-17 10:31:25 -04:00
_constructed = false;
clear();
2010-04-23 17:35:05 -04:00
from(cfg);
2011-06-14 16:22:51 -04:00
if( force ) {
version += rand() % 100000 + 10000;
}
configAssert( version < 0 /*unspecified*/ || (version >= 1) );
2010-07-08 14:41:49 -04:00
if( version < 1 )
version = 1;
2010-04-23 17:35:05 -04:00
_ok = true;
2011-07-17 10:31:25 -04:00
_constructed = true;
2010-04-23 17:35:05 -04:00
}
2010-04-22 18:43:37 -04:00
ReplSetConfig::ReplSetConfig(const HostAndPort& h) {
2011-07-17 10:31:25 -04:00
_constructed = false;
clear();
2010-04-21 17:40:24 -04:00
int level = 2;
DEV level = 0;
BSONObj cfg;
2010-05-13 17:18:17 -04:00
int v = -5;
2010-04-21 17:40:24 -04:00
try {
if( h.isSelf() ) {
2010-05-13 11:03:23 -04:00
;
}
else {
2010-04-22 18:43:37 -04:00
/* first, make sure other node is configured to be a replset. just to be safe. */
2010-08-02 15:21:26 -04:00
string setname = cmdLine.ourSetName();
2010-05-09 17:29:35 -04:00
BSONObj cmd = BSON( "replSetHeartbeat" << setname );
2010-05-13 11:03:23 -04:00
int theirVersion;
2010-04-22 18:43:37 -04:00
BSONObj info;
2010-12-06 17:12:40 -05:00
log() << "trying to contact " << h.toString() << rsLog;
bool ok = requestHeartbeat(setname, "", h.toString(), info, -2, theirVersion);
2011-01-04 00:40:41 -05:00
if( info["rs"].trueValue() ) {
2010-05-13 17:18:17 -04:00
// yes, it is a replicate set, although perhaps not yet initialized
2010-05-13 11:03:23 -04:00
}
2010-05-13 17:18:17 -04:00
else {
if( !ok ) {
log() << "replSet TEMP !ok heartbeating " << h.toString() << " on cfg load" << rsLog;
2011-01-04 00:40:41 -05:00
if( !info.isEmpty() )
2010-07-12 08:19:03 -04:00
log() << "replSet info " << h.toString() << " : " << info.toString() << rsLog;
2010-05-13 17:18:17 -04:00
return;
}
2011-01-04 00:40:41 -05:00
{
2010-05-13 17:18:17 -04:00
stringstream ss;
ss << "replSet error: member " << h.toString() << " is not in --replSet mode";
msgassertedNoTrace(13260, ss.str().c_str()); // not caught as not a user exception - we want it not caught
//for python err# checker: uassert(13260, "", false);
}
2010-04-22 18:43:37 -04:00
}
}
2010-05-13 17:18:17 -04:00
v = -4;
unsigned long long count = 0;
try {
ScopedConn conn(h.toString());
v = -3;
cfg = conn.findOne(rsConfigNs, Query()).getOwned();
count = conn.count(rsConfigNs);
}
2010-09-28 14:02:27 -04:00
catch ( DBException& ) {
if ( !h.isSelf() ) {
throw;
}
// on startup, socket is not listening yet
DBDirectClient cli;
cfg = cli.findOne( rsConfigNs, Query() ).getOwned();
count = cli.count(rsConfigNs);
}
if( count > 1 )
uasserted(13109, str::stream() << "multiple rows in " << rsConfigNs << " not supported host: " << h.toString());
2011-01-04 00:40:41 -05:00
if( cfg.isEmpty() ) {
2010-05-13 17:18:17 -04:00
version = EMPTYCONFIG;
2010-04-21 17:40:24 -04:00
return;
2010-04-22 16:17:18 -04:00
}
version = -1;
2010-04-21 17:40:24 -04:00
}
2011-01-04 00:40:41 -05:00
catch( DBException& e) {
2010-05-13 17:18:17 -04:00
version = v;
2010-06-26 20:29:39 -04:00
log(level) << "replSet load config couldn't get from " << h.toString() << ' ' << e.what() << rsLog;
2010-04-21 17:40:24 -04:00
return;
}
2010-04-21 17:13:25 -04:00
from(cfg);
2010-08-26 13:47:27 -04:00
checkRsConfig();
2010-04-21 17:40:24 -04:00
_ok = true;
log(level) << "replSet load config ok from " << (h.isSelf() ? "self" : h.toString()) << rsLog;
2011-07-17 10:31:25 -04:00
_constructed = true;
2010-04-21 16:14:28 -04:00
}
2010-04-20 21:55:34 -04:00
2010-04-21 16:14:28 -04:00
}