Ejemplo n.º 1
0
 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;
 }
Ejemplo n.º 2
0
/**
 * 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;
}