virtual bool run(const string& dbname, BSONObj& cmdObj, 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; } } if ( cmdObj["preCondition"].type() == Array ){ BSONObjIterator i( cmdObj["preCondition"].Obj() ); while ( i.more() ){ BSONObj f = i.next().Obj(); BSONObj realres = db.findOne( f["ns"].String() , f["q"].Obj() ); 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; BSONObjIterator i( ops ); while ( i.more() ){ BSONElement e = i.next(); applyOperation_inlock( e.Obj() , false ); num++; } result.append( "applied" , num ); 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"; logOp( "c" , tempNS.c_str() , cmdObj.firstElement().wrap() ); } return true; }
void ReplSource::applyOperation(const BSONObj& op) { try { bool failedUpdate = applyOperation_inlock( op ); if (failedUpdate) { Sync sync(hostName); if (sync.shouldRetry(op)) { uassert(15914, "Failure retrying initial sync update", !applyOperation_inlock(op)); } } } catch ( UserException& e ) { log() << "sync: caught user assertion " << e << " while applying op: " << op << endl;; } catch ( DBException& e ) { log() << "sync: caught db exception " << e << " while applying op: " << op << endl;; } }
void ReplSource::applyOperation(const BSONObj& op) { try { applyOperation_inlock( op ); } catch ( UserException& e ) { log() << "sync: caught user assertion " << e << " while applying op: " << op << endl;; } catch ( DBException& e ) { log() << "sync: caught db exception " << e << " while applying op: " << op << endl;; } }
void run() { create(); BSONObjBuilder b; b.appendTimestamp("ts", OpTime::now().asLL()); b.append("op", "u"); b.append("o", BSON("$set" << BSON("x" << 456))); b.append("o2", BSON("_id" << 123 << "x" << 123)); b.append("ns", _ns); // in an annoying twist of api, returns true on failure assert(applyOperation_inlock(b.obj(), true)); }
void ReplSetImpl::syncApply(const BSONObj &o) { //const char *op = o.getStringField("op"); char db[MaxDatabaseLen]; const char *ns = o.getStringField("ns"); nsToDatabase(ns, db); if ( *ns == '.' || *ns == 0 ) { log() << "replSet skipping bad op in oplog: " << o.toString() << endl; return; } Client::Context ctx(ns); ctx.getClient()->curop()->reset(); /* todo : if this asserts, do we want to ignore or not? */ applyOperation_inlock(o); }
/* apply the log op that is in param o @return bool success (true) or failure (false) */ bool SyncTail::syncApply( OperationContext* txn, const BSONObj &op, bool convertUpdateToUpsert) { const char *ns = op.getStringField("ns"); verify(ns); if ( (*ns == '\0') || (*ns == '.') ) { // this is ugly // this is often a no-op // but can't be 100% sure if( *op.getStringField("op") != 'n' ) { error() << "replSet skipping bad op in oplog: " << op.toString() << rsLog; } return true; } bool isCommand(op["op"].valuestrsafe()[0] == 'c'); boost::scoped_ptr<Lock::ScopedLock> lk; if(isCommand) { // a command may need a global write lock. so we will conservatively go // ahead and grab one here. suboptimal. :-( lk.reset(new Lock::GlobalWrite(txn->lockState())); } else { // DB level lock for this operation lk.reset(new Lock::DBWrite(txn->lockState(), ns)); } Client::Context ctx(ns); ctx.getClient()->curop()->reset(); // For non-initial-sync, we convert updates to upserts // to suppress errors when replaying oplog entries. bool ok = !applyOperation_inlock(txn, ctx.db(), op, true, convertUpdateToUpsert); opsAppliedStats.increment(); txn->recoveryUnit()->commitIfNeeded(); return ok; }
virtual bool run(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; } } if ( cmdObj["preCondition"].type() == Array ) { BSONObjIterator i( cmdObj["preCondition"].Obj() ); while ( i.more() ) { BSONObj f = i.next().Obj(); BSONObj realres = db.findOne( f["ns"].String() , f["q"].Obj() ); 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(); Client::Context ctx(temp["ns"].String()); bool failed = applyOperation_inlock(temp, false, alwaysUpsert); ab.append(!failed); if ( failed ) errors++; num++; } 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); } } logOp("c", tempNS.c_str(), cmdBuilder.done()); } return errors == 0; }
virtual bool run(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; DurTransaction 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(); 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(ns); invariant(Lock::nested()); Client::Context ctx(ns); bool failed = 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); } } logOp(&txn, "c", tempNS.c_str(), cmdBuilder.done()); } return errors == 0; }
// returns true on success, false on failure bool apply(const BSONObj& op) { Client::Context ctx( _cappedNs ); OperationContextImpl txn; // in an annoying twist of api, returns true on failure return !applyOperation_inlock(&txn, ctx.db(), op, true); }
// returns true on success, false on failure bool apply(const BSONObj& op) { Client::Context ctx( _ns ); // in an annoying twist of api, returns true on failure return !applyOperation_inlock(op, true); }