Esempio n. 1
0
    /*@ @param opstr:
          c userCreateNS
          i insert
          n no-op / keepalive
          d delete / remove
          u update
    */
    void logOp(OperationContext* txn,
               const char* opstr,
               const char* ns,
               const BSONObj& obj,
               BSONObj* patt,
               bool* b,
               bool fromMigrate) {
        try {
            if ( getGlobalReplicationCoordinator()->isReplEnabled() ) {
                _logOp(txn, opstr, ns, 0, obj, patt, b, fromMigrate);
            }

            logOpForSharding(txn, opstr, ns, obj, patt, fromMigrate);
            logOpForDbHash(ns);
            getGlobalAuthorizationManager()->logOp(opstr, ns, obj, patt, b);

            if ( strstr( ns, ".system.js" ) ) {
                Scope::storedFuncMod(); // this is terrible
            }
        }
        catch (const DBException& ex) {
            severe() << "Fatal DBException in logOp(): " << ex.toString();
            std::terminate();
        }
        catch (const std::exception& ex) {
            severe() << "Fatal std::exception in logOp(): " << ex.what();
            std::terminate();
        }
        catch (...) {
            severe() << "Fatal error in logOp()";
            std::terminate();
        }
    }
Esempio n. 2
0
void OpObserver::onDropDatabase(OperationContext* txn, const std::string& dbName) {
    BSONObj cmdObj = BSON("dropDatabase" << 1);

    repl::logOp(txn, "c", dbName.c_str(), cmdObj, nullptr, false);

    getGlobalAuthorizationManager()->logOp(txn, "c", dbName.c_str(), cmdObj, nullptr);
    logOpForDbHash(txn, dbName.c_str());
}
Esempio n. 3
0
void OpObserver::onApplyOps(OperationContext* txn,
                            const std::string& dbName,
                            const BSONObj& applyOpCmd) {
    repl::logOp(txn, "c", dbName.c_str(), applyOpCmd, nullptr, false);

    getGlobalAuthorizationManager()->logOp(txn, "c", dbName.c_str(), applyOpCmd, nullptr);
    logOpForDbHash(txn, dbName.c_str());
}
Esempio n. 4
0
void OpObserver::onDropIndex(OperationContext* txn,
                             const std::string& dbName,
                             const BSONObj& idxDescriptor) {
    repl::logOp(txn, "c", dbName.c_str(), idxDescriptor, nullptr, false);

    getGlobalAuthorizationManager()->logOp(txn, "c", dbName.c_str(), idxDescriptor, nullptr);
    logOpForDbHash(txn, dbName.c_str());
}
Esempio n. 5
0
void OpObserver::onCreateIndex(OperationContext* txn,
                               const std::string& ns,
                               BSONObj indexDoc,
                               bool fromMigrate) {
    repl::logOp(txn, "i", ns.c_str(), indexDoc, nullptr, fromMigrate);

    getGlobalAuthorizationManager()->logOp(txn, "i", ns.c_str(), indexDoc, nullptr);
    logOpForSharding(txn, "i", ns.c_str(), indexDoc, nullptr, fromMigrate);
    logOpForDbHash(txn, ns.c_str());
}
Esempio n. 6
0
void OpObserver::onUpdate(OperationContext* txn, oplogUpdateEntryArgs args) {
    repl::logOp(txn, "u", args.ns.c_str(), args.update, &args.criteria, args.fromMigrate);

    getGlobalAuthorizationManager()->logOp(txn, "u", args.ns.c_str(), args.update, &args.criteria);
    logOpForSharding(txn, "u", args.ns.c_str(), args.update, &args.criteria, args.fromMigrate);
    logOpForDbHash(txn, args.ns.c_str());
    if (strstr(args.ns.c_str(), ".system.js")) {
        Scope::storedFuncMod(txn);
    }
}
Esempio n. 7
0
void OpObserver::onEmptyCapped(OperationContext* txn, const NamespaceString& collectionName) {
    std::string dbName = collectionName.db().toString() + ".$cmd";
    BSONObj cmdObj = BSON("emptycapped" << collectionName.coll());

    if (!collectionName.isSystemDotProfile()) {
        // do not replicate system.profile modifications
        repl::logOp(txn, "c", dbName.c_str(), cmdObj, nullptr, false);
    }

    getGlobalAuthorizationManager()->logOp(txn, "c", dbName.c_str(), cmdObj, nullptr);
    logOpForDbHash(txn, dbName.c_str());
}
Esempio n. 8
0
void OpObserver::onInsert(OperationContext* txn,
                          const NamespaceString& ns,
                          BSONObj doc,
                          bool fromMigrate) {
    repl::logOp(txn, "i", ns.ns().c_str(), doc, nullptr, fromMigrate);

    getGlobalAuthorizationManager()->logOp(txn, "i", ns.ns().c_str(), doc, nullptr);
    logOpForSharding(txn, "i", ns.ns().c_str(), doc, nullptr, fromMigrate);
    logOpForDbHash(txn, ns.ns().c_str());
    if (strstr(ns.ns().c_str(), ".system.js")) {
        Scope::storedFuncMod(txn);
    }
}
Esempio n. 9
0
void OpObserver::onDelete(OperationContext* txn,
                          const std::string& ns,
                          const BSONObj& idDoc,
                          bool fromMigrate) {
    repl::logOp(txn, "d", ns.c_str(), idDoc, nullptr, fromMigrate);

    getGlobalAuthorizationManager()->logOp(txn, "d", ns.c_str(), idDoc, nullptr);
    logOpForSharding(txn, "d", ns.c_str(), idDoc, nullptr, fromMigrate);
    logOpForDbHash(txn, ns.c_str());
    if (strstr(ns.c_str(), ".system.js")) {
        Scope::storedFuncMod(txn);
    }
}
Esempio n. 10
0
void OpObserver::onCollMod(OperationContext* txn,
                           const std::string& dbName,
                           const BSONObj& collModCmd) {
    BSONElement first = collModCmd.firstElement();
    std::string coll = first.valuestr();

    if (!NamespaceString(NamespaceString(dbName).db(), coll).isSystemDotProfile()) {
        // do not replicate system.profile modifications
        repl::logOp(txn, "c", dbName.c_str(), collModCmd, nullptr, false);
    }

    getGlobalAuthorizationManager()->logOp(txn, "c", dbName.c_str(), collModCmd, nullptr);
    logOpForDbHash(txn, dbName.c_str());
}
Esempio n. 11
0
void OpObserver::onRenameCollection(OperationContext* txn,
                                    const NamespaceString& fromCollection,
                                    const NamespaceString& toCollection,
                                    bool dropTarget,
                                    bool stayTemp) {
    std::string dbName = fromCollection.db().toString() + ".$cmd";
    BSONObj cmdObj =
        BSON("renameCollection" << fromCollection.ns() << "to" << toCollection.ns() << "stayTemp"
                                << stayTemp << "dropTarget" << dropTarget);

    repl::logOp(txn, "c", dbName.c_str(), cmdObj, nullptr, false);

    getGlobalAuthorizationManager()->logOp(txn, "c", dbName.c_str(), cmdObj, nullptr);
    logOpForDbHash(txn, dbName.c_str());
}
Esempio n. 12
0
void OpObserver::onCreateCollection(OperationContext* txn,
                                    const NamespaceString& collectionName,
                                    const CollectionOptions& options) {
    std::string dbName = collectionName.db().toString() + ".$cmd";
    BSONObjBuilder b;
    b.append("create", collectionName.coll().toString());
    b.appendElements(options.toBSON());
    BSONObj cmdObj = b.obj();

    if (!collectionName.isSystemDotProfile()) {
        // do not replicate system.profile modifications
        repl::logOp(txn, "c", dbName.c_str(), cmdObj, nullptr, false);
    }

    getGlobalAuthorizationManager()->logOp(txn, "c", dbName.c_str(), cmdObj, nullptr);
    logOpForDbHash(txn, dbName.c_str());
}
Esempio n. 13
0
    /*@ @param opstr:
          c userCreateNS
          i insert
          n no-op / keepalive
          d delete / remove
          u update
    */
    void logOp(OperationContext* txn,
               const char* opstr,
               const char* ns,
               const BSONObj& obj,
               BSONObj* patt,
               bool* b,
               bool fromMigrate) {
        try {
            // TODO SERVER-15192 remove this once all listeners are rollback-safe.
            class RollbackPreventer : public RecoveryUnit::Change {
                virtual void commit() {}
                virtual void rollback() {
                    severe() << "Rollback of logOp not currently allowed (SERVER-15192)";
                    fassertFailed(18805);
                }
            };
            txn->recoveryUnit()->registerChange(new RollbackPreventer());

            if ( getGlobalReplicationCoordinator()->isReplEnabled() ) {
                _logOp(txn, opstr, ns, 0, obj, patt, b, fromMigrate);
            }

            logOpForSharding(txn, opstr, ns, obj, patt, fromMigrate);
            logOpForDbHash(ns);
            getGlobalAuthorizationManager()->logOp(opstr, ns, obj, patt, b);

            if ( strstr( ns, ".system.js" ) ) {
                Scope::storedFuncMod(); // this is terrible
            }
        }
        catch (const DBException& ex) {
            severe() << "Fatal DBException in logOp(): " << ex.toString();
            std::terminate();
        }
        catch (const std::exception& ex) {
            severe() << "Fatal std::exception in logOp(): " << ex.what();
            std::terminate();
        }
        catch (...) {
            severe() << "Fatal error in logOp()";
            std::terminate();
        }
    }
Esempio n. 14
0
    /*@ @param opstr:
          c userCreateNS
          i insert
          n no-op / keepalive
          d delete / remove
          u update
    */
    void logOp(OperationContext* txn,
               const char* opstr,
               const char* ns,
               const BSONObj& obj,
               BSONObj* patt,
               bool* b,
               bool fromMigrate) {
        if ( replSettings.master ) {
            _logOp(txn, opstr, ns, 0, obj, patt, b, fromMigrate);
        }

        logOpForSharding(txn, opstr, ns, obj, patt, fromMigrate);
        logOpForDbHash(ns);
        getGlobalAuthorizationManager()->logOp(opstr, ns, obj, patt, b);

        if ( strstr( ns, ".system.js" ) ) {
            Scope::storedFuncMod(); // this is terrible
        }

    }
Esempio n. 15
0
        virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {

            if ( cmdObj.firstElement().type() != Array ) {
                errmsg = "ops has to be an array";
                return false;
            }

            BSONObj ops = cmdObj.firstElement().Obj();

            {
                // check input
                BSONObjIterator i( ops );
                while ( i.more() ) {
                    BSONElement e = i.next();
                    if (!_checkOperation(e, errmsg)) {
                        return false;
                    }
                }
            }

            // SERVER-4328 todo : is global ok or does this take a long time? i believe multiple 
            // ns used so locking individually requires more analysis
            Lock::GlobalWrite globalWriteLock(txn->lockState());

            DBDirectClient db(txn);

            // Preconditions check reads the database state, so needs to be done locked
            if ( cmdObj["preCondition"].type() == Array ) {
                BSONObjIterator i( cmdObj["preCondition"].Obj() );
                while ( i.more() ) {
                    BSONObj f = i.next().Obj();

                    DBDirectClient db( txn );
                    BSONObj realres = db.findOne( f["ns"].String() , f["q"].Obj() );

                    // Apply-ops would never have a $where matcher, so use the default callback,
                    // which will throw an error if $where is found.
                    Matcher m(f["res"].Obj());
                    if ( ! m.matches( realres ) ) {
                        result.append( "got" , realres );
                        result.append( "whatFailed" , f );
                        errmsg = "pre-condition failed";
                        return false;
                    }
                }
            }

            // apply
            int num = 0;
            int errors = 0;
            
            BSONObjIterator i( ops );
            BSONArrayBuilder ab;
            const bool alwaysUpsert = cmdObj.hasField("alwaysUpsert") ?
                    cmdObj["alwaysUpsert"].trueValue() : true;
            
            while ( i.more() ) {
                BSONElement e = i.next();
                const BSONObj& temp = e.Obj();

                // Ignore 'n' operations.
                const char *opType = temp["op"].valuestrsafe();
                if (*opType == 'n') continue;

                string ns = temp["ns"].String();

                // Run operations under a nested lock as a hack to prevent yielding.
                //
                // The list of operations is supposed to be applied atomically; yielding
                // would break atomicity by allowing an interruption or a shutdown to occur
                // after only some operations are applied.  We are already locked globally
                // at this point, so taking a DBLock on the namespace creates a nested lock,
                // and yields are disallowed for operations that hold a nested lock.
                //
                // We do not have a wrapping WriteUnitOfWork so it is possible for a journal
                // commit to happen with a subset of ops applied.
                // TODO figure out what to do about this.
                Lock::DBLock lk(txn->lockState(), nsToDatabaseSubstring(ns), MODE_X);
                invariant(txn->lockState()->isRecursive());

                Client::Context ctx(txn, ns);
                bool failed = repl::applyOperation_inlock(txn,
                                                          ctx.db(),
                                                          temp,
                                                          false,
                                                          alwaysUpsert);
                ab.append(!failed);
                if ( failed )
                    errors++;

                num++;

                logOpForDbHash(ns.c_str());
            }

            result.append( "applied" , num );
            result.append( "results" , ab.arr() );

            if ( ! fromRepl ) {
                // We want this applied atomically on slaves
                // so we re-wrap without the pre-condition for speed

                string tempNS = str::stream() << dbname << ".$cmd";

                // TODO: possibly use mutable BSON to remove preCondition field
                // once it is available
                BSONObjIterator iter(cmdObj);
                BSONObjBuilder cmdBuilder;

                while (iter.more()) {
                    BSONElement elem(iter.next());
                    if (strcmp(elem.fieldName(), "preCondition") != 0) {
                        cmdBuilder.append(elem);
                    }
                }

                // We currently always logOp the command regardless of whether the individial ops
                // succeeded and rely on any failures to also happen on secondaries. This isn't
                // perfect, but it's what the command has always done and is part of its "correct"
                // behavior.
                WriteUnitOfWork wunit(txn);
                repl::logOp(txn, "c", tempNS.c_str(), cmdBuilder.done());
                wunit.commit();
            }

            if (errors != 0) {
                return false;
            }

            return true;
        }
Esempio n. 16
0
        virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {

            if ( cmdObj.firstElement().type() != Array ) {
                errmsg = "ops has to be an array";
                return false;
            }

            BSONObj ops = cmdObj.firstElement().Obj();

            {
                // check input
                BSONObjIterator i( ops );
                while ( i.more() ) {
                    BSONElement e = i.next();
                    if ( e.type() == Object )
                        continue;
                    errmsg = "op not an object: ";
                    errmsg += e.fieldName();
                    return false;
                }
            }

            // SERVER-4328 todo : is global ok or does this take a long time? i believe multiple 
            // ns used so locking individually requires more analysis
            Lock::GlobalWrite globalWriteLock(txn->lockState());

            // Preconditions check reads the database state, so needs to be done locked
            if ( cmdObj["preCondition"].type() == Array ) {
                BSONObjIterator i( cmdObj["preCondition"].Obj() );
                while ( i.more() ) {
                    BSONObj f = i.next().Obj();

                    DBDirectClient db( txn );
                    BSONObj realres = db.findOne( f["ns"].String() , f["q"].Obj() );

                    // Apply-ops would never have a $where matcher, so use the default callback,
                    // which will throw an error if $where is found.
                    Matcher m(f["res"].Obj());
                    if ( ! m.matches( realres ) ) {
                        result.append( "got" , realres );
                        result.append( "whatFailed" , f );
                        errmsg = "pre-condition failed";
                        return false;
                    }
                }
            }

            // apply
            int num = 0;
            int errors = 0;
            
            BSONObjIterator i( ops );
            BSONArrayBuilder ab;
            const bool alwaysUpsert = cmdObj.hasField("alwaysUpsert") ?
                    cmdObj["alwaysUpsert"].trueValue() : true;
            
            while ( i.more() ) {
                BSONElement e = i.next();
                const BSONObj& temp = e.Obj();

                string ns = temp["ns"].String();

                // Run operations under a nested lock as a hack to prevent them from yielding.
                //
                // The list of operations is supposed to be applied atomically; yielding would break
                // atomicity by allowing an interruption or a shutdown to occur after only some
                // operations are applied.  We are already locked globally at this point, so taking
                // a DBWrite on the namespace creates a nested lock, and yields are disallowed for
                // operations that hold a nested lock.
                Lock::DBWrite lk(txn->lockState(), ns);
                invariant(txn->lockState()->isRecursive());

                Client::Context ctx(ns);
                bool failed = repl::applyOperation_inlock(txn,
                                                             ctx.db(),
                                                             temp,
                                                             false,
                                                             alwaysUpsert);
                ab.append(!failed);
                if ( failed )
                    errors++;

                num++;

                logOpForDbHash(ns.c_str());
            }

            result.append( "applied" , num );
            result.append( "results" , ab.arr() );

            if ( ! fromRepl ) {
                // We want this applied atomically on slaves
                // so we re-wrap without the pre-condition for speed

                string tempNS = str::stream() << dbname << ".$cmd";

                // TODO: possibly use mutable BSON to remove preCondition field
                // once it is available
                BSONObjIterator iter(cmdObj);
                BSONObjBuilder cmdBuilder;

                while (iter.more()) {
                    BSONElement elem(iter.next());
                    if (strcmp(elem.fieldName(), "preCondition") != 0) {
                        cmdBuilder.append(elem);
                    }
                }

                repl::logOp(txn, "c", tempNS.c_str(), cmdBuilder.done());
            }

            return errors == 0;
        }
Esempio n. 17
0
        virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {

            if ( cmdObj.firstElement().type() != Array ) {
                errmsg = "ops has to be an array";
                return false;
            }

            BSONObj ops = cmdObj.firstElement().Obj();

            {
                // check input
                BSONObjIterator i( ops );
                while ( i.more() ) {
                    BSONElement e = i.next();
                    if (!_checkOperation(e, errmsg)) {
                        return false;
                    }
                }
            }

            // SERVER-4328 todo : is global ok or does this take a long time? i believe multiple 
            // ns used so locking individually requires more analysis
            ScopedTransaction scopedXact(txn, MODE_X);
            Lock::GlobalWrite globalWriteLock(txn->lockState());

            if (!fromRepl &&
                !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(dbname)) {
                return appendCommandStatus(result, Status(ErrorCodes::NotMaster, str::stream()
                    << "Not primary while applying ops to database " << dbname));
            }

            // Preconditions check reads the database state, so needs to be done locked
            if ( cmdObj["preCondition"].type() == Array ) {
                BSONObjIterator i( cmdObj["preCondition"].Obj() );
                while ( i.more() ) {
                    BSONObj f = i.next().Obj();

                    DBDirectClient db( txn );
                    BSONObj realres = db.findOne( f["ns"].String() , f["q"].Obj() );

                    // Apply-ops would never have a $where matcher, so use the default callback,
                    // which will throw an error if $where is found.
                    Matcher m(f["res"].Obj());
                    if ( ! m.matches( realres ) ) {
                        result.append( "got" , realres );
                        result.append( "whatFailed" , f );
                        errmsg = "pre-condition failed";
                        return false;
                    }
                }
            }

            // apply
            int num = 0;
            int errors = 0;
            
            BSONObjIterator i( ops );
            BSONArrayBuilder ab;
            const bool alwaysUpsert = cmdObj.hasField("alwaysUpsert") ?
                    cmdObj["alwaysUpsert"].trueValue() : true;
            
            while ( i.more() ) {
                BSONElement e = i.next();
                const BSONObj& temp = e.Obj();

                // Ignore 'n' operations.
                const char *opType = temp["op"].valuestrsafe();
                if (*opType == 'n') continue;

                const string ns = temp["ns"].String();

                // Run operations under a nested lock as a hack to prevent yielding.
                //
                // The list of operations is supposed to be applied atomically; yielding
                // would break atomicity by allowing an interruption or a shutdown to occur
                // after only some operations are applied.  We are already locked globally
                // at this point, so taking a DBLock on the namespace creates a nested lock,
                // and yields are disallowed for operations that hold a nested lock.
                //
                // We do not have a wrapping WriteUnitOfWork so it is possible for a journal
                // commit to happen with a subset of ops applied.
                // TODO figure out what to do about this.
                Lock::GlobalWrite globalWriteLockDisallowTempRelease(txn->lockState());

                // Ensures that yielding will not happen (see the comment above).
                DEV {
                    Locker::LockSnapshot lockSnapshot;
                    invariant(!txn->lockState()->saveLockStateAndUnlock(&lockSnapshot));
                };

                OldClientContext ctx(txn, ns);

                Status status(ErrorCodes::InternalError, "");
                while (true) {
                    try {
                        // We assume that in the WriteConflict retry case, either the op rolls back
                        // any changes it makes or is otherwise safe to rerun.
                        status =
                            repl::applyOperation_inlock(txn, ctx.db(), temp, false, alwaysUpsert);
                        break;
                    }
                    catch (const WriteConflictException& wce) {
                        LOG(2) << "WriteConflictException in applyOps command, retrying.";
                        txn->recoveryUnit()->commitAndRestart();
                        continue;
                    }
                }

                ab.append(status.isOK());
                if (!status.isOK()) {
                    errors++;
                }

                num++;

                WriteUnitOfWork wuow(txn);
                logOpForDbHash(txn, ns.c_str());
                wuow.commit();
            }

            result.append( "applied" , num );
            result.append( "results" , ab.arr() );

            if ( ! fromRepl ) {
                // We want this applied atomically on slaves
                // so we re-wrap without the pre-condition for speed

                string tempNS = str::stream() << dbname << ".$cmd";

                // TODO: possibly use mutable BSON to remove preCondition field
                // once it is available
                BSONObjIterator iter(cmdObj);
                BSONObjBuilder cmdBuilder;

                while (iter.more()) {
                    BSONElement elem(iter.next());
                    if (strcmp(elem.fieldName(), "preCondition") != 0) {
                        cmdBuilder.append(elem);
                    }
                }

                const BSONObj cmdRewritten = cmdBuilder.done();

                // We currently always logOp the command regardless of whether the individial ops
                // succeeded and rely on any failures to also happen on secondaries. This isn't
                // perfect, but it's what the command has always done and is part of its "correct"
                // behavior.
                while (true) {
                    try {
                        WriteUnitOfWork wunit(txn);
                        getGlobalEnvironment()->getOpObserver()->onApplyOps(txn,
                                                                            tempNS,
                                                                            cmdRewritten);
                        wunit.commit();
                        break;
                    }
                    catch (const WriteConflictException& wce) {
                        LOG(2) <<
                            "WriteConflictException while logging applyOps command, retrying.";
                        txn->recoveryUnit()->commitAndRestart();
                        continue;
                    }
                }
            }

            if (errors != 0) {
                return false;
            }

            return true;
        }