summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands/write_commands
diff options
context:
space:
mode:
authorEliot Horowitz <eliot@10gen.com>2014-10-19 15:05:43 -0400
committerEliot Horowitz <eliot@10gen.com>2014-10-19 15:15:46 -0400
commit74835f660d9315024994877de3c40cd542625fa1 (patch)
treef8a95324403aec4d90ff506962acd026bfafe40d /src/mongo/db/commands/write_commands
parent64b7bc99aee07bf04f189c1f06e1756645f04b71 (diff)
downloadmongo-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.cpp91
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));
}
}