diff options
author | Dianna Hohensee <dianna.hohensee@10gen.com> | 2016-02-16 10:13:49 -0500 |
---|---|---|
committer | Dianna Hohensee <dianna.hohensee@10gen.com> | 2016-02-18 17:32:49 -0500 |
commit | 626080cb8eb8b336b660ebafc460b79aa8447685 (patch) | |
tree | be8b9e65b97a6d0e76fd5efc67c71ddf203003a2 | |
parent | dfec3b92b8713ac5654ff0204c63b28883ed0b53 (diff) | |
download | mongo-626080cb8eb8b336b660ebafc460b79aa8447685.tar.gz |
SERVER-22239 DuplicateKey Error, update optime to latest after insert failure
(cherry picked from commit ee31993039163d1b0a055f90a8f76093e694d7dd)
-rw-r--r-- | jstests/replsets/lastop.js | 19 | ||||
-rw-r--r-- | src/mongo/db/commands/write_commands/batch_executor.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/instance.cpp | 13 |
3 files changed, 42 insertions, 3 deletions
diff --git a/jstests/replsets/lastop.js b/jstests/replsets/lastop.js index 4d1b2519cd3..f3eca2ccb3d 100644 --- a/jstests/replsets/lastop.js +++ b/jstests/replsets/lastop.js @@ -106,18 +106,31 @@ assert.eq(noOp, twelfthOp); + // No-op insert + assert.writeOK(m1.getCollection("test.foo").insert({ _id : 5, x : 5 })); + var thirteenthOp = m1.getCollection("test.foo").getDB().getLastErrorObj().lastOp; + + assert.writeOK(m2.getCollection("test.foo").insert({ m2 : 991 })); + var fourteenthOp = m2.getCollection("test.foo").getDB().getLastErrorObj().lastOp; + + // Hits DuplicateKey error and fails insert -- no-op + assert.writeError(m1.getCollection("test.foo").insert({ _id : 5, x : 5 })); + noOp = m1.getCollection("test.foo").getDB().getLastErrorObj().lastOp; + + assert.eq(noOp, fourteenthOp); + // Test update and delete failures in legacy write mode. m2.forceWriteMode('legacy'); m1.forceWriteMode('legacy'); m2.getCollection("test.foo").insert({ m2 : 995 }); - var thirteenthOp = m2.getCollection("test.foo").getDB().getLastErrorObj().lastOp; + var fifthteenthOp = m2.getCollection("test.foo").getDB().getLastErrorObj().lastOp; m1.getCollection("test.foo").remove({ m1 : 1 }); noOp = m1.getCollection("test.foo").getDB().getLastErrorObj().lastOp; - assert.eq(noOp, thirteenthOp); + assert.eq(noOp, fifthteenthOp); m1.getCollection("test.foo").update({ m1 : 1 }, {$set: {m1: 4}}); noOp = m1.getCollection("test.foo").getDB().getLastErrorObj().lastOp; - assert.eq(noOp, thirteenthOp); + assert.eq(noOp, fifthteenthOp); })(); diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp index 74717698950..34eb38a13f2 100644 --- a/src/mongo/db/commands/write_commands/batch_executor.cpp +++ b/src/mongo/db/commands/write_commands/batch_executor.cpp @@ -817,6 +817,12 @@ void WriteBatchExecutor::execInserts(const BatchedCommandRequest& request, currentOp->debug().ninserted = 0; } + auto client = _txn->getClient(); + auto lastOpAtOperationStart = repl::ReplClientInfo::forClient(client).getLastOp(); + ScopeGuard lastOpSetterGuard = MakeObjGuard(repl::ReplClientInfo::forClient(client), + &repl::ReplClientInfo::setLastOpToSystemLastOpTime, + _txn); + int64_t chunkCount = 0; int64_t chunkBytes = 0; const int64_t chunkMaxCount = internalQueryExecYieldIterations / 2; @@ -853,6 +859,13 @@ void WriteBatchExecutor::execInserts(const BatchedCommandRequest& request, } } + if (repl::ReplClientInfo::forClient(client).getLastOp() != lastOpAtOperationStart) { + // If this operation has already generated a new lastOp, don't bother setting it + // here. No-op updates will not generate a new lastOp, so we still need the guard to + // fire in that case. + lastOpSetterGuard.Dismiss(); + } + // TODO: Move Top and CurOp metrics management into an RAII object. currentOp->done(); recordCurOpMetrics(_txn); diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index 70ee99959e4..b3b10e42ebd 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -1017,6 +1017,12 @@ NOINLINE_DECL void insertMulti(OperationContext* txn, int64_t chunkCount = 0; int64_t chunkSize = 0; + auto client = txn->getClient(); + auto lastOpAtOperationStart = repl::ReplClientInfo::forClient(client).getLastOp(); + ScopeGuard lastOpSetterGuard = MakeObjGuard(repl::ReplClientInfo::forClient(client), + &repl::ReplClientInfo::setLastOpToSystemLastOpTime, + txn); + for (vector<BSONObj>::iterator it = docs.begin(); it != docs.end(); it++) { StatusWith<BSONObj> fixed = fixDocumentForInsert(*it); uassertStatusOK(fixed.getStatus()); @@ -1041,6 +1047,13 @@ NOINLINE_DECL void insertMulti(OperationContext* txn, } if (chunkBegin != docs.end()) insertMultiVector(txn, ctx, keepGoing, ns, op, chunkBegin, docs.end()); + + if (repl::ReplClientInfo::forClient(client).getLastOp() != lastOpAtOperationStart) { + // If this operation has already generated a new lastOp, don't bother setting it + // here. No-op inserts will not generate a new lastOp, so we still need the + // guard to fire in that case. + lastOpSetterGuard.Dismiss(); + } } static void convertSystemIndexInsertsToCommands(DbMessage& d, BSONArrayBuilder* allCmdsBuilder) { |