bool canStartRollback(OplogReader& r, GTID idToRollbackTo) { shared_ptr<DBClientConnection> conn(r.conn_shared()); // before we start rollback, let's make sure that the minUnapplied on the remote // server is past the id that we are rolling back to. Otherwise, the snapshot // we create will not be up to date, and the rollback algorithm will not work BSONObjBuilder b; b.append("_id", "minUnapplied"); // Note that another way to get this information is to // request a heartbeat. That one will technically return // a more up to date value for minUnapplied BSONObj res = findOneFromConn(conn.get(), rsReplInfo, Query(b.done())); GTID minUnapplied = getGTIDFromBSON("GTID", res); if (GTID::cmp(minUnapplied, idToRollbackTo) < 0) { log() << "Remote server has minUnapplied " << minUnapplied.toString() << \ " we want to rollback to " << idToRollbackTo.toString() << \ ". Therefore, exiting and retrying." << rsLog; return false; } return true; }
/** * Do the initial sync for this member. * This code can use a little refactoring. bit ugly */ bool ReplSetImpl::_syncDoInitialSync() { sethbmsg("initial sync pending",0); bool needsFullSync = gtidManager->getLiveState().isInitial(); bool needGapsFilled = needsFullSync || replSettings.fastsync; // if this is the first node, it may have already become primary if ( box.getState().primary() ) { sethbmsg("I'm already primary, no need for initial sync",0); return true; } const Member *source = NULL; OplogReader r; string sourceHostname; // only bother making a connection if we need to connect for some reason if (needGapsFilled) { source = getMemberToSyncTo(); if (!source) { sethbmsg("initial sync need a member to be primary or secondary to do our initial sync", 0); sleepsecs(15); return false; } sourceHostname = source->h().toString(); if( !r.connect(sourceHostname, 0) ) { sethbmsg( str::stream() << "initial sync couldn't connect to " << source->h().toString() , 0); sleepsecs(15); return false; } } if( needsFullSync ) { BSONObj lastOp = r.getLastOp(); if( lastOp.isEmpty() ) { sethbmsg("initial sync couldn't read remote oplog", 0); sleepsecs(15); return false; } { LOCK_REASON(lockReason, "repl: initial sync drop all databases"); Lock::GlobalWrite lk(lockReason); Client::Transaction dropTransaction(DB_SERIALIZABLE); sethbmsg("initial sync drop all databases", 0); dropAllDatabasesExceptLocal(); dropTransaction.commit(); } // now deal with creation of oplog // first delete any existing data in the oplog { LOCK_REASON(lockReason, "repl: create oplog"); Lock::DBWrite lk("local", lockReason); Client::Transaction fileOpsTransaction(DB_SERIALIZABLE); deleteOplogFiles(); fileOpsTransaction.commit(0); } try { sethbmsg("initial sync clone all databases", 0); shared_ptr<DBClientConnection> conn(r.conn_shared()); RemoteTransaction rtxn(*conn, "mvcc"); list<string> dbs = conn->getDatabaseNamesForRepl(); // // Not sure if it is necessary to have a separate fileOps // transaction and clone transaction. The cloneTransaction // has a higher chance of failing, and I don't know at the moment // if it is ok to do fileops successfully, and then an operation (cloning) that // later causes an abort. So, to be cautious, they are separate { LOCK_REASON(lockReason, "repl: initial sync"); Lock::GlobalWrite lk(lockReason); Client::Transaction cloneTransaction(DB_SERIALIZABLE); bool ret = _syncDoInitialSync_clone(sourceHostname.c_str(), dbs, conn); if (!ret) { veto(source->fullName(), 600); cloneTransaction.abort(); sleepsecs(300); return false; } // at this point, we have copied all of the data from the // remote machine. Now we need to copy the replication information // on the remote machine's local database, we need to copy // the entire (small) replInfo dictionary, and the necessary portion // of the oplog // first copy the replInfo, as we will use its information // to determine how much of the opLog to copy { Client::Context ctx( "local" ); BSONObj q; cloneCollection(conn, "local", rsReplInfo, q, true, //copyIndexes false //logForRepl ); // copy entire oplog (probably overkill) cloneCollection(conn, "local", rsoplog, q, true, //copyIndexes false //logForRepl ); // copy entire oplog.refs (probably overkill) cloneCollection(conn, "local", rsOplogRefs, q, true, //copyIndexes false //logForRepl ); } cloneTransaction.commit(0); } bool ok = rtxn.commit(); verify(ok); // absolutely no reason this should fail, it was read only // data should now be consistent } catch (DBException &e) { sethbmsg("exception trying to copy data", 0); LOG(0) << e.getCode() << ": " << e.what() << endl; sleepsecs(1); return false; } } if (needGapsFilled) { _fillGaps(&r); } GTID dummy; applyMissingOpsInOplog(dummy); sethbmsg("initial sync done",0); return true; }