void ReplSet::haveNewConfig(ReplSetConfig& newConfig, bool addComment) { bo comment; if( addComment ) comment = BSON( "msg" << "Reconfig set" << "version" << newConfig.version ); newConfig.saveConfigLocally(comment); try { BSONObj oldConfForAudit = config().asBson(); BSONObj newConfForAudit = newConfig.asBson(); audit::logReplSetReconfig(ClientBasic::getCurrent(), &oldConfForAudit, &newConfForAudit); if (initFromConfig(newConfig, true)) { log() << "replSet replSetReconfig new config saved locally" << rsLog; } } catch(DBException& e) { log() << "replSet error unexpected exception in haveNewConfig() : " << e.toString() << rsLog; _fatal(); } catch(...) { log() << "replSet error unexpected exception in haveNewConfig()" << rsLog; _fatal(); } }
StatusWith<int> validateConfigForInitiate(ReplicationCoordinatorExternalState* externalState, const ReplSetConfig& newConfig, ServiceContext* ctx) { Status status = newConfig.validate(); if (!status.isOK()) { return StatusWith<int>(status); } status = newConfig.checkIfWriteConcernCanBeSatisfied(newConfig.getDefaultWriteConcern()); if (!status.isOK()) { return status.withContext( "Found invalid default write concern in 'getLastErrorDefaults' field"); } status = validateArbiterPriorities(newConfig); if (!status.isOK()) { return StatusWith<int>(status); } if (newConfig.getConfigVersion() != 1) { return StatusWith<int>(ErrorCodes::NewReplicaSetConfigurationIncompatible, str::stream() << "Configuration used to initiate a replica set must " << " have version 1, but found " << newConfig.getConfigVersion()); } return findSelfInConfigIfElectable(externalState, newConfig, ctx); }
// Our own config must be the first one. bool ReplSetImpl::_loadConfigFinish(OperationContext* txn, vector<ReplSetConfig*>& cfgs) { int v = -1; ReplSetConfig *highest = 0; int myVersion = -2000; int n = 0; for (vector<ReplSetConfig*>::iterator i = cfgs.begin(); i != cfgs.end(); i++) { ReplSetConfig* cfg = *i; DEV { LOG(1) << n+1 << " config shows version " << cfg->version << rsLog; } if (++n == 1) myVersion = cfg->version; if (cfg->ok() && cfg->version > v) { highest = cfg; v = cfg->version; } } verify(highest); if (!initFromConfig(txn, *highest)) return false; if (highest->version > myVersion && highest->version >= 0) { log() << "replSet got config version " << highest->version << " from a remote, saving locally" << rsLog; highest->saveConfigLocally(txn, BSONObj()); } return true; }
StatusWith<int> validateConfigForReconfig(ReplicationCoordinatorExternalState* externalState, const ReplSetConfig& oldConfig, const ReplSetConfig& newConfig, ServiceContext* ctx, bool force) { Status status = newConfig.validate(); if (!status.isOK()) { return StatusWith<int>(status); } status = newConfig.checkIfWriteConcernCanBeSatisfied(newConfig.getDefaultWriteConcern()); if (!status.isOK()) { return status.withContext( "Found invalid default write concern in 'getLastErrorDefaults' field"); } status = validateOldAndNewConfigsCompatible(oldConfig, newConfig); if (!status.isOK()) { return StatusWith<int>(status); } status = validateArbiterPriorities(newConfig); if (!status.isOK()) { return StatusWith<int>(status); } if (force) { return findSelfInConfig(externalState, newConfig, ctx); } return findSelfInConfigIfElectable(externalState, newConfig, ctx); }
void ReplSet::haveNewConfig(OperationContext* txn, ReplSetConfig& newConfig, bool addComment) { bo comment; if( addComment ) comment = BSON( "msg" << "Reconfig set" << "version" << newConfig.version ); newConfig.saveConfigLocally(txn, comment); try { BSONObj oldConfForAudit = config().asBson(); BSONObj newConfForAudit = newConfig.asBson(); audit::logReplSetReconfig(ClientBasic::getCurrent(), &oldConfForAudit, &newConfForAudit); if (initFromConfig(txn, newConfig, true)) { log() << "replSet replSetReconfig new config saved locally" << rsLog; } } catch (const DBException& e) { log() << "replSet error unexpected exception in haveNewConfig() : " << e.toString() << rsLog; fassertFailedNoTrace(18755); } catch (...) { std::terminate(); } }
OplogFetcher::OplogFetcher(executor::TaskExecutor* executor, OpTimeWithHash lastFetched, HostAndPort source, NamespaceString nss, ReplSetConfig config, std::size_t maxFetcherRestarts, int requiredRBID, bool requireFresherSyncSource, DataReplicatorExternalState* dataReplicatorExternalState, EnqueueDocumentsFn enqueueDocumentsFn, OnShutdownCallbackFn onShutdownCallbackFn, const int batchSize) : AbstractOplogFetcher(executor, lastFetched, source, nss, maxFetcherRestarts, onShutdownCallbackFn, "oplog fetcher"), _metadataObject(makeMetadataObject(config.getProtocolVersion() == 1LL)), _requiredRBID(requiredRBID), _requireFresherSyncSource(requireFresherSyncSource), _dataReplicatorExternalState(dataReplicatorExternalState), _enqueueDocumentsFn(enqueueDocumentsFn), _awaitDataTimeout(calculateAwaitDataTimeout(config)), _batchSize(batchSize) { invariant(config.isInitialized()); invariant(enqueueDocumentsFn); }
// Our own config must be the first one. bool ReplSetImpl::_loadConfigFinish(vector<ReplSetConfig>& cfgs) { int v = -1; ReplSetConfig *highest = 0; int myVersion = -2000; int n = 0; for( vector<ReplSetConfig>::iterator i = cfgs.begin(); i != cfgs.end(); i++ ) { ReplSetConfig& cfg = *i; if( ++n == 1 ) myVersion = cfg.version; if( cfg.ok() && cfg.version > v ) { highest = &cfg; v = cfg.version; } } assert( highest ); if( !initFromConfig(*highest) ) return false; if( highest->version > myVersion && highest->version >= 0 ) { log() << "replSet got config version " << highest->version << " from a remote, saving locally" << rsLog; writelock lk("admin."); highest->saveConfigLocally(BSONObj()); } return true; }
Status checkQuorumForReconfig(executor::TaskExecutor* executor, const ReplSetConfig& rsConfig, const int myIndex, long long term) { invariant(rsConfig.getConfigVersion() > 1); return checkQuorumGeneral(executor, rsConfig, myIndex, term, "reconfig quorum check"); }
void ReplSet::haveNewConfig(ReplSetConfig& newConfig, bool addComment) { bo comment; if( addComment ) comment = BSON( "msg" << "Reconfig set" << "version" << newConfig.version ); newConfig.saveConfigLocally(comment); try { if (initFromConfig(newConfig, true)) { log() << "replSet replSetReconfig new config saved locally" << rsLog; } } catch(DBException& e) { if( e.getCode() == 13497 /* removed from set */ ) { cc().shutdown(); dbexit( EXIT_CLEAN , "removed from replica set" ); // never returns assert(0); } log() << "replSet error unexpected exception in haveNewConfig() : " << e.toString() << rsLog; _fatal(); } catch(...) { log() << "replSet error unexpected exception in haveNewConfig()" << rsLog; _fatal(); } }
Status checkQuorumForInitiate(executor::TaskExecutor* executor, const ReplSetConfig& rsConfig, const int myIndex, long long term) { invariant(rsConfig.getConfigVersion() == 1); return checkQuorumGeneral(executor, rsConfig, myIndex, term); }
StatusWith<int> validateConfigForStartUp(ReplicationCoordinatorExternalState* externalState, const ReplSetConfig& newConfig, ServiceContext* ctx) { Status status = newConfig.validate(); if (!status.isOK()) { return StatusWith<int>(status); } return findSelfInConfig(externalState, newConfig, ctx); }
void ReplSet::haveNewConfig(ReplSetConfig& newConfig, bool addComment) { lock l(this); // convention is to lock replset before taking the db rwlock writelock lk(""); bo comment; if( addComment ) comment = BSON( "msg" << "Reconfig set" << "version" << newConfig.version ); newConfig.saveConfigLocally(comment); try { initFromConfig(newConfig, true); log() << "replSet replSetReconfig new config saved locally" << rsLog; } catch(DBException& e) { log() << "replSet error unexpected exception in haveNewConfig() : " << e.toString() << rsLog; _fatal(); } catch(...) { log() << "replSet error unexpected exception in haveNewConfig()" << rsLog; _fatal(); } }
void ReplSet::haveNewConfig(ReplSetConfig& newConfig, bool addComment) { bo comment; if( addComment ) comment = BSON( "msg" << "Reconfig set" << "version" << newConfig.version ); newConfig.saveConfigLocally(comment); try { if (initFromConfig(newConfig, true)) { log() << "replSet replSetReconfig new config saved locally" << rsLog; } } catch(DBException& e) { log() << "replSet error unexpected exception in haveNewConfig() : " << e.toString() << rsLog; _fatal(); } catch(...) { log() << "replSet error unexpected exception in haveNewConfig()" << rsLog; _fatal(); } }
// @param reconf true if this is a reconfiguration and not an initial load of the configuration. // @return true if ok; throws if config really bad; false if config doesn't include self bool ReplSetImpl::initFromConfig(OperationContext* txn, ReplSetConfig& c, bool reconf) { // NOTE: haveNewConfig() writes the new config to disk before we get here. So // we cannot error out at this point, except fatally. Check errors earlier. lock lk(this); if (!getLastErrorDefault.isEmpty() || !c.getLastErrorDefaults.isEmpty()) { getLastErrorDefault = c.getLastErrorDefaults; } list<ReplSetConfig::MemberCfg*> newOnes; // additive short-cuts the new config setup. If we are just adding a // node/nodes and nothing else is changing, this is additive. If it's // not a reconfig, we're not adding anything bool additive = reconf; bool updateConfigs = false; { unsigned nfound = 0; int me = 0; for (vector<ReplSetConfig::MemberCfg>::iterator i = c.members.begin(); i != c.members.end(); i++) { ReplSetConfig::MemberCfg& m = *i; if (isSelf(m.h)) { me++; } if (reconf) { const Member *old = findById(m._id); if (old) { nfound++; verify((int) old->id() == m._id); if (!old->config().isSameIgnoringTags(m)) { additive = false; } if (!updateConfigs && old->config() != m) { updateConfigs = true; } } else { newOnes.push_back(&m); } } } if (me == 0) { // we're not in the config -- we must have been removed if (state().removed()) { // already took note of our ejection from the set // so just sit tight and poll again return false; } _members.orphanAll(); // kill off rsHealthPoll threads (because they Know Too Much about our past) endOldHealthTasks(); // clear sync target to avoid faulty sync attempts; we must do this before we // close sockets, since that will trigger the bgsync thread to reconnect. BackgroundSync::get()->clearSyncTarget(); // close sockets to force clients to re-evaluate this member MessagingPort::closeAllSockets(0); // take note of our ejection changeState(MemberState::RS_REMOVED); // go into holding pattern log() << "replSet info self not present in the repl set configuration:" << rsLog; log() << c.toString() << rsLog; loadConfig(txn); // redo config from scratch return false; } uassert(13302, "replSet error self appears twice in the repl set configuration", me<=1); if (state().removed()) { // If we were removed and have now been added back in, switch state. changeState(MemberState::RS_RECOVERING); } // if we found different members that the original config, reload everything if (reconf && config().members.size() != nfound) additive = false; } // If we are changing chaining rules, we don't want this to be an additive reconfig so that // the primary can step down and the sync targets change. // TODO: This can be removed once SERVER-5208 is fixed. if (reconf && config().chainingAllowed() != c.chainingAllowed()) { additive = false; } _cfg = new ReplSetConfig(c); // config() is same thing but const, so we use that when we can for clarity below dassert(&config() == _cfg); verify(config().ok()); verify(_name.empty() || _name == config()._id); _name = config()._id; verify(!_name.empty()); // this is a shortcut for simple changes if (additive) { log() << "replSet info : additive change to configuration" << rsLog; if (updateConfigs) { // we have new configs for existing members, so we need to repopulate _members // with the most recent configs _members.orphanAll(); // for logging string members = ""; // not setting _self to 0 as other threads use _self w/o locking int me = 0; for(vector<ReplSetConfig::MemberCfg>::const_iterator i = config().members.begin(); i != config().members.end(); i++) { const ReplSetConfig::MemberCfg& m = *i; Member *mi; members += (members == "" ? "" : ", ") + m.h.toString(); if (isSelf(m.h)) { verify(me++ == 0); mi = new Member(m.h, m._id, &m, true); setSelfTo(mi); } else { mi = new Member(m.h, m._id, &m, false); _members.push(mi); } } // trigger a handshake to update the syncSource of our writeconcern information syncSourceFeedback.forwardSlaveHandshake(); } // add any new members for (list<ReplSetConfig::MemberCfg*>::const_iterator i = newOnes.begin(); i != newOnes.end(); i++) { ReplSetConfig::MemberCfg *m = *i; Member *mi = new Member(m->h, m->_id, m, false); // we will indicate that new members are up() initially so that we don't relinquish // our primary state because we can't (transiently) see a majority. they should be // up as we check that new members are up before getting here on reconfig anyway. mi->get_hbinfo().health = 0.1; _members.push(mi); startHealthTaskFor(mi); } // if we aren't creating new members, we may have to update the // groups for the current ones _cfg->updateMembers(_members); return true; } // start with no members. if this is a reconfig, drop the old ones. _members.orphanAll(); endOldHealthTasks(); int oldPrimaryId = -1; { const Member *p = box.getPrimary(); if (p) oldPrimaryId = p->id(); } forgetPrimary(txn); // not setting _self to 0 as other threads use _self w/o locking int me = 0; // For logging string members = ""; for (vector<ReplSetConfig::MemberCfg>::const_iterator i = config().members.begin(); i != config().members.end(); i++) { const ReplSetConfig::MemberCfg& m = *i; Member *mi; members += (members == "" ? "" : ", ") + m.h.toString(); if (isSelf(m.h)) { verify(me++ == 0); mi = new Member(m.h, m._id, &m, true); if (!reconf) { log() << "replSet I am " << m.h.toString() << rsLog; } setSelfTo(mi); if ((int)mi->id() == oldPrimaryId) box.setSelfPrimary(mi); } else { mi = new Member(m.h, m._id, &m, false); _members.push(mi); if ((int)mi->id() == oldPrimaryId) box.setOtherPrimary(mi); } } if (me == 0){ log() << "replSet warning did not detect own host in full reconfig, members " << members << " config: " << c << rsLog; } else { // Do this after we've found ourselves, since _self needs // to be set before we can start the heartbeat tasks for (Member *mb = _members.head(); mb; mb=mb->next()) { startHealthTaskFor(mb); } } return true; }
/** @param reconf true if this is a reconfiguration and not an initial load of the configuration. @return true if ok; throws if config really bad; false if config doesn't include self */ bool ReplSetImpl::initFromConfig(ReplSetConfig& c, bool reconf) { /* NOTE: haveNewConfig() writes the new config to disk before we get here. So we cannot error out at this point, except fatally. Check errors earlier. */ lock lk(this); if( getLastErrorDefault || !c.getLastErrorDefaults.isEmpty() ) { // see comment in dbcommands.cpp for getlasterrordefault getLastErrorDefault = new BSONObj( c.getLastErrorDefaults ); } list<const ReplSetConfig::MemberCfg*> newOnes; bool additive = reconf; { unsigned nfound = 0; int me = 0; for( vector<ReplSetConfig::MemberCfg>::iterator i = c.members.begin(); i != c.members.end(); i++ ) { const ReplSetConfig::MemberCfg& m = *i; if( m.h.isSelf() ) { nfound++; me++; if( !reconf || (_self && _self->id() == (unsigned) m._id) ) ; else { log() << "replSet " << _self->id() << ' ' << m._id << rsLog; assert(false); } } else if( reconf ) { const Member *old = findById(m._id); if( old ) { nfound++; assert( (int) old->id() == m._id ); if( old->config() == m ) { additive = false; } } else { newOnes.push_back(&m); } } } if( me == 0 ) { // log() << "replSet config : " << _cfg->toString() << rsLog; log() << "replSet error self not present in the repl set configuration:" << rsLog; log() << c.toString() << rsLog; uasserted(13497, "replSet error self not present in the configuration"); } uassert( 13302, "replSet error self appears twice in the repl set configuration", me<=1 ); if( reconf && config().members.size() != nfound ) additive = false; } _cfg = new ReplSetConfig(c); assert( _cfg->ok() ); assert( _name.empty() || _name == _cfg->_id ); _name = _cfg->_id; assert( !_name.empty() ); if( additive ) { log() << "replSet info : additive change to configuration" << rsLog; for( list<const ReplSetConfig::MemberCfg*>::const_iterator i = newOnes.begin(); i != newOnes.end(); i++ ) { const ReplSetConfig::MemberCfg* m = *i; Member *mi = new Member(m->h, m->_id, m, false); /** we will indicate that new members are up() initially so that we don't relinquish our primary state because we can't (transiently) see a majority. they should be up as we check that new members are up before getting here on reconfig anyway. */ mi->get_hbinfo().health = 0.1; _members.push(mi); startHealthTaskFor(mi); } return true; } // start with no members. if this is a reconfig, drop the old ones. _members.orphanAll(); endOldHealthTasks(); int oldPrimaryId = -1; { const Member *p = box.getPrimary(); if( p ) oldPrimaryId = p->id(); } forgetPrimary(); setSelfTo(0); for( vector<ReplSetConfig::MemberCfg>::iterator i = _cfg->members.begin(); i != _cfg->members.end(); i++ ) { const ReplSetConfig::MemberCfg& m = *i; Member *mi; if( m.h.isSelf() ) { assert( _self == 0 ); mi = new Member(m.h, m._id, &m, true); setSelfTo(mi); if( (int)mi->id() == oldPrimaryId ) box.setSelfPrimary(mi); } else { mi = new Member(m.h, m._id, &m, false); _members.push(mi); startHealthTaskFor(mi); if( (int)mi->id() == oldPrimaryId ) box.setOtherPrimary(mi); } } return true; }
/** @param reconf true if this is a reconfiguration and not an initial load of the configuration. @return true if ok; throws if config really bad; false if config doesn't include self */ bool ReplSetImpl::initFromConfig(ReplSetConfig& c, bool reconf) { /* NOTE: haveNewConfig() writes the new config to disk before we get here. So we cannot error out at this point, except fatally. Check errors earlier. */ lock lk(this); if( getLastErrorDefault || !c.getLastErrorDefaults.isEmpty() ) { // see comment in dbcommands.cpp for getlasterrordefault getLastErrorDefault = new BSONObj( c.getLastErrorDefaults ); } list<const ReplSetConfig::MemberCfg*> newOnes; // additive short-cuts the new config setup. If we are just adding a // node/nodes and nothing else is changing, this is additive. If it's // not a reconfig, we're not adding anything bool additive = reconf; { unsigned nfound = 0; int me = 0; for( vector<ReplSetConfig::MemberCfg>::iterator i = c.members.begin(); i != c.members.end(); i++ ) { const ReplSetConfig::MemberCfg& m = *i; if( m.h.isSelf() ) { me++; } if( reconf ) { if (m.h.isSelf() && (!_self || (int)_self->id() != m._id)) { log() << "self doesn't match: " << m._id << rsLog; assert(false); } const Member *old = findById(m._id); if( old ) { nfound++; assert( (int) old->id() == m._id ); if( old->config() != m ) { additive = false; } } else { newOnes.push_back(&m); } } } if( me == 0 ) { _members.orphanAll(); // hbs must continue to pick up new config // stop sync thread box.set(MemberState::RS_STARTUP, 0); // go into holding pattern log() << "replSet error self not present in the repl set configuration:" << rsLog; log() << c.toString() << rsLog; return false; } uassert( 13302, "replSet error self appears twice in the repl set configuration", me<=1 ); // if we found different members that the original config, reload everything if( reconf && config().members.size() != nfound ) additive = false; } _cfg = new ReplSetConfig(c); assert( _cfg->ok() ); assert( _name.empty() || _name == _cfg->_id ); _name = _cfg->_id; assert( !_name.empty() ); // this is a shortcut for simple changes if( additive ) { log() << "replSet info : additive change to configuration" << rsLog; for( list<const ReplSetConfig::MemberCfg*>::const_iterator i = newOnes.begin(); i != newOnes.end(); i++ ) { const ReplSetConfig::MemberCfg* m = *i; Member *mi = new Member(m->h, m->_id, m, false); /** we will indicate that new members are up() initially so that we don't relinquish our primary state because we can't (transiently) see a majority. they should be up as we check that new members are up before getting here on reconfig anyway. */ mi->get_hbinfo().health = 0.1; _members.push(mi); startHealthTaskFor(mi); } return true; } // start with no members. if this is a reconfig, drop the old ones. _members.orphanAll(); endOldHealthTasks(); int oldPrimaryId = -1; { const Member *p = box.getPrimary(); if( p ) oldPrimaryId = p->id(); } forgetPrimary(); // not setting _self to 0 as other threads use _self w/o locking int me = 0; // For logging string members = ""; for( vector<ReplSetConfig::MemberCfg>::iterator i = _cfg->members.begin(); i != _cfg->members.end(); i++ ) { const ReplSetConfig::MemberCfg& m = *i; Member *mi; members += ( members == "" ? "" : ", " ) + m.h.toString(); if( m.h.isSelf() ) { assert( me++ == 0 ); mi = new Member(m.h, m._id, &m, true); setSelfTo(mi); if( (int)mi->id() == oldPrimaryId ) box.setSelfPrimary(mi); } else { mi = new Member(m.h, m._id, &m, false); _members.push(mi); startHealthTaskFor(mi); if( (int)mi->id() == oldPrimaryId ) box.setOtherPrimary(mi); } } if( me == 0 ) { log() << "replSet warning did not detect own host in full reconfig, members " << members << " config: " << c << rsLog; } return true; }