allow initial sync from secondaries SERVER-1829

This commit is contained in:
Kristina Chodorow
2010-11-09 17:28:32 -05:00
parent 8e348439b2
commit 2683812ba8
7 changed files with 331 additions and 25 deletions

View File

@@ -63,8 +63,8 @@ namespace mongo {
/* todo : progress metering to sethbmsg. */
static bool clone(const char *master, string db) {
string err;
return cloneFrom(master, err, db, false,
/*slaveok later can be true*/ false, true, false);
return cloneFrom(master, err, db, false,
/* slave_ok */ true, true, false);
}
void _logOpObjRS(const BSONObj& op);
@@ -103,23 +103,60 @@ namespace mongo {
*/
}
void ReplSetImpl::_syncDoInitialSync() {
sethbmsg("initial sync pending",0);
/**
* Choose a member to sync from. Prefers a secondary, if available.
*
* TODO: make a config setting like "cloneFromPrimary" or something to force
* machines to clone from the primary. We might want to integrate this with
* tags functionality, if we only want it to be able to clone from certain
* machines.
*/
const Member* ReplSetImpl::getMemberToSyncTo() {
for( Member *m = head(); m; m = m->next() ) {
if (m->hbinfo().up() && m->state() == MemberState::RS_SECONDARY) {
sethbmsg( str::stream() << "syncing to secondary: " << m->fullName(), 0);
return const_cast<Member*>(m);
}
}
// can't find secondary, try primary
StateBox::SP sp = box.get();
assert( !sp.state.primary() ); // wouldn't make sense if we were.
const Member *cp = sp.primary;
if( cp == 0 ) {
sethbmsg("initial sync need a member to be primary to do our initial sync", 0);
// we just checked in _syncDoInitialSync, but we could have lost a
// primary since then
if (!sp.primary) {
return 0;
}
sethbmsg( str::stream() << "syncing to primary: " << sp.primary->fullName(), 0);
return const_cast<Member*>(sp.primary);
}
/**
* Do the initial sync for this member. There must be a primary available
* for the whole intial sync, even if we're syncing from a secondary.
*/
void ReplSetImpl::_syncDoInitialSync() {
sethbmsg("initial sync pending",0);
const Member *cp = box.getPrimary();
if (!cp) {
sethbmsg("initial sync needs a member to be primary to begin");
sleepsecs(15);
return;
}
string masterHostname = cp->h().toString();
const Member *source = getMemberToSyncTo();
if (!source) {
sethbmsg("initial sync need a member to be primary or secondary to do our initial sync", 0);
sleepsecs(15);
return;
}
string sourceHostname = source->h().toString();
OplogReader r;
if( !r.connect(masterHostname) ) {
sethbmsg( str::stream() << "initial sync couldn't connect to " << cp->h().toString() , 0);
if( !r.connect(sourceHostname) ) {
sethbmsg( str::stream() << "initial sync couldn't connect to " << source->h().toString() , 0);
sleepsecs(15);
return;
}
@@ -150,7 +187,7 @@ namespace mongo {
{
writelock lk(db);
Client::Context ctx(db);
ok = clone(masterHostname.c_str(), db);
ok = clone(sourceHostname.c_str(), db);
}
if( !ok ) {
sethbmsg( str::stream() << "initial sync error clone of " << db << " failed sleeping 5 minutes" ,0);
@@ -177,7 +214,7 @@ namespace mongo {
{
sethbmsg("initial sync initial oplog application");
isyncassert( "initial sync source must remain primary throughout our initial sync [2]", box.getPrimary() == cp );
if( ! initialSyncOplogApplication(/*primary*/cp, /*applyGTE*/startingTS, /*minValid*/mvoptime) ) { // note we assume here that this call does not throw
if( ! initialSyncOplogApplication(source, /*applyGTE*/startingTS, /*minValid*/mvoptime) ) { // note we assume here that this call does not throw
log() << "replSet initial sync failed during applyoplog" << rsLog;
emptyOplog(); // otherwise we'll be up!
lastOpTimeWritten = OpTime();