diff options
author | Eliot Horowitz <eliot@10gen.com> | 2014-10-19 15:05:43 -0400 |
---|---|---|
committer | Eliot Horowitz <eliot@10gen.com> | 2014-10-19 15:15:46 -0400 |
commit | 74835f660d9315024994877de3c40cd542625fa1 (patch) | |
tree | f8a95324403aec4d90ff506962acd026bfafe40d /src/mongo/db/commands/write_commands | |
parent | 64b7bc99aee07bf04f189c1f06e1756645f04b71 (diff) | |
download | mongo-74835f660d9315024994877de3c40cd542625fa1.tar.gz |
SERVER-14668: pull collection creation even higher on update path to avoid deadlock
Diffstat (limited to 'src/mongo/db/commands/write_commands')
-rw-r--r-- | src/mongo/db/commands/write_commands/batch_executor.cpp | 91 |
1 files changed, 66 insertions, 25 deletions
diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp index 4e0eac5a579..160a660bdee 100644 --- a/src/mongo/db/commands/write_commands/batch_executor.cpp +++ b/src/mongo/db/commands/write_commands/batch_executor.cpp @@ -1143,38 +1143,79 @@ namespace mongo { return; } - /////////////////////////////////////////// - Lock::DBLock dbLock(txn->lockState(), nsString.db(), MODE_IX); - Lock::CollectionLock colLock(txn->lockState(), - nsString.ns(), - isMulti ? MODE_X : MODE_IX); - /////////////////////////////////////////// + bool createCollection = false; + for ( int fakeLoop = 0; fakeLoop < 1; fakeLoop++ ) { + + if ( createCollection ) { + Lock::DBLock lk(txn->lockState(), nsString.db(), MODE_X); + Client::Context ctx(txn, nsString.ns(), false /* don't check version */); + Database* db = ctx.db(); + if ( db->getCollection( txn, nsString.ns() ) ) { + // someone else beat us to it + } + else { + WriteUnitOfWork wuow(txn); + uassertStatusOK( userCreateNS( txn, db, + nsString.ns(), BSONObj(), + !request.isFromReplication() ) ); + wuow.commit(); + } + } - if (!checkShardVersion(txn, &shardingState, *updateItem.getRequest(), result)) - return; + /////////////////////////////////////////// + Lock::DBLock dbLock(txn->lockState(), nsString.db(), MODE_IX); + Lock::CollectionLock colLock(txn->lockState(), + nsString.ns(), + isMulti ? MODE_X : MODE_IX); + /////////////////////////////////////////// - Client::Context ctx(txn, nsString.ns(), false /* don't check version */); + if (!checkShardVersion(txn, &shardingState, *updateItem.getRequest(), result)) + return; - try { - UpdateResult res = executor.execute(ctx.db()); + Client::Context ctx(txn, nsString.ns(), false /* don't check version */); - const long long numDocsModified = res.numDocsModified; - const long long numMatched = res.numMatched; - const BSONObj resUpsertedID = res.upserted; + if ( ctx.db()->getCollection( txn, nsString.ns() ) == NULL ) { + if ( createCollection ) { + // we raced with some, accept defeat + result->getStats().nModified = 0; + result->getStats().n = 0; + return; + } - // We have an _id from an insert - const bool didInsert = !resUpsertedID.isEmpty(); + if ( !request.isUpsert() ) { + // not an upsert, not collection, nothing to do + result->getStats().nModified = 0; + result->getStats().n = 0; + return; + } - result->getStats().nModified = didInsert ? 0 : numDocsModified; - result->getStats().n = didInsert ? 1 : numMatched; - result->getStats().upsertedID = resUpsertedID; - } - catch (const DBException& ex) { - status = ex.toStatus(); - if (ErrorCodes::isInterruption(status.code())) { - throw; + // upsert, mark that we should create collection + fakeLoop = -1; + createCollection = true; + continue; + } + + try { + UpdateResult res = executor.execute(ctx.db()); + + const long long numDocsModified = res.numDocsModified; + const long long numMatched = res.numMatched; + const BSONObj resUpsertedID = res.upserted; + + // We have an _id from an insert + const bool didInsert = !resUpsertedID.isEmpty(); + + result->getStats().nModified = didInsert ? 0 : numDocsModified; + result->getStats().n = didInsert ? 1 : numMatched; + result->getStats().upsertedID = resUpsertedID; + } + catch (const DBException& ex) { + status = ex.toStatus(); + if (ErrorCodes::isInterruption(status.code())) { + throw; + } + result->setError(toWriteError(status)); } - result->setError(toWriteError(status)); } } |