summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDianna Hohensee <dianna.hohensee@10gen.com>2016-02-16 10:13:49 -0500
committerDianna Hohensee <dianna.hohensee@10gen.com>2016-02-18 17:32:49 -0500
commit626080cb8eb8b336b660ebafc460b79aa8447685 (patch)
treebe8b9e65b97a6d0e76fd5efc67c71ddf203003a2
parentdfec3b92b8713ac5654ff0204c63b28883ed0b53 (diff)
downloadmongo-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.js19
-rw-r--r--src/mongo/db/commands/write_commands/batch_executor.cpp13
-rw-r--r--src/mongo/db/instance.cpp13
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) {