diff options
author | Jack Mulrow <jack.mulrow@mongodb.com> | 2017-09-15 17:57:33 -0400 |
---|---|---|
committer | Jack Mulrow <jack.mulrow@mongodb.com> | 2017-09-19 10:29:48 -0400 |
commit | 9e598d7fbcb9c7e7d7b1ace7c9df47e316deb02f (patch) | |
tree | e33ab79da5c19674b7493d10bdd06a23462c4a6c | |
parent | 86f4e7955327bc009dd4a25135bb741886d0ccaa (diff) | |
download | mongo-9e598d7fbcb9c7e7d7b1ace7c9df47e316deb02f.tar.gz |
SERVER-30206 Error if retryable write request has multi update or delete
-rw-r--r-- | jstests/replsets/transaction_table_oplog_replay.js | 8 | ||||
-rw-r--r-- | jstests/sharding/retryable_writes.js | 50 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_exec.cpp | 8 |
3 files changed, 62 insertions, 4 deletions
diff --git a/jstests/replsets/transaction_table_oplog_replay.js b/jstests/replsets/transaction_table_oplog_replay.js index 8587558bc57..dbe039dc087 100644 --- a/jstests/replsets/transaction_table_oplog_replay.js +++ b/jstests/replsets/transaction_table_oplog_replay.js @@ -180,10 +180,10 @@ { delete: "foo", deletes: [ - {q: {_id: 10}, limit: 0}, - {q: {_id: 20}, limit: 0}, - {q: {_id: 30}, limit: 0}, - {q: {_id: 40}, limit: 0} + {q: {_id: 10}, limit: 1}, + {q: {_id: 20}, limit: 1}, + {q: {_id: 30}, limit: 1}, + {q: {_id: 40}, limit: 1} ], ordered: true, lsid: {id: UUID()}, diff --git a/jstests/sharding/retryable_writes.js b/jstests/sharding/retryable_writes.js index 83c0964f845..adaccecc104 100644 --- a/jstests/sharding/retryable_writes.js +++ b/jstests/sharding/retryable_writes.js @@ -295,6 +295,54 @@ assert.eq(1, collContents[1].y); } + function runMultiTests(mainConn, priConn) { + // Test the behavior of retryable writes with multi=true / limit=0 + var lsid = {id: UUID()}; + var testDb = mainConn.getDB('test_multi'); + + // Only the update statements with multi=true in a batch fail. + var cmd = { + update: 'user', + updates: [{q: {x: 1}, u: {y: 1}}, {q: {x: 2}, u: {z: 1}, multi: true}], + ordered: true, + lsid: lsid, + txnNumber: NumberLong(1), + }; + var res = assert.commandWorked(testDb.runCommand(cmd)); + assert.eq(1, + res.writeErrors.length, + 'expected only one write error, received: ' + tojson(res.writeErrors)); + assert.eq(1, + res.writeErrors[0].index, + 'expected the update at index 1 to fail, not the update at index: ' + + res.writeErrors[0].index); + assert.eq(ErrorCodes.InvalidOptions, + res.writeErrors[0].code, + 'expected to fail with code ' + ErrorCodes.InvalidOptions + ', received: ' + + res.writeErrors[0].code); + + // Only the delete statements with limit=0 in a batch fail. + cmd = { + delete: 'user', + deletes: [{q: {x: 1}, limit: 1}, {q: {y: 1}, limit: 0}], + ordered: false, + lsid: lsid, + txnNumber: NumberLong(1), + }; + res = assert.commandWorked(testDb.runCommand(cmd)); + assert.eq(1, + res.writeErrors.length, + 'expected only one write error, received: ' + tojson(res.writeErrors)); + assert.eq(1, + res.writeErrors[0].index, + 'expected the delete at index 1 to fail, not the delete at index: ' + + res.writeErrors[0].index); + assert.eq(ErrorCodes.InvalidOptions, + res.writeErrors[0].code, + 'expected to fail with code ' + ErrorCodes.InvalidOptions + ', received: ' + + res.writeErrors[0].code); + } + // Tests for replica set var replTest = new ReplSetTest({nodes: 1}); replTest.startSet(); @@ -304,6 +352,7 @@ runTests(priConn, priConn); runFailpointTests(priConn, priConn); + runMultiTests(priConn, priConn); replTest.stopSet(); @@ -312,6 +361,7 @@ runTests(st.s0, st.rs0.getPrimary()); runFailpointTests(st.s0, st.rs0.getPrimary()); + runMultiTests(st.s0, st.rs0.getPrimary()); st.stop(); })(); diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp index 24dfef674a7..d3a9813039b 100644 --- a/src/mongo/db/ops/write_ops_exec.cpp +++ b/src/mongo/db/ops/write_ops_exec.cpp @@ -521,6 +521,10 @@ static SingleWriteResult performSingleUpdateOp(OperationContext* opCtx, const NamespaceString& ns, StmtId stmtId, const write_ops::UpdateOpEntry& op) { + uassert(ErrorCodes::InvalidOptions, + "Cannot use (or request) retryable writes with multi=true", + !(opCtx->getTxnNumber() && op.getMulti())); + globalOpCounters.gotUpdate(); auto& curOp = *CurOp::get(opCtx); { @@ -663,6 +667,10 @@ static SingleWriteResult performSingleDeleteOp(OperationContext* opCtx, const NamespaceString& ns, StmtId stmtId, const write_ops::DeleteOpEntry& op) { + uassert(ErrorCodes::InvalidOptions, + "Cannot use (or request) retryable writes with limit=0", + !(opCtx->getTxnNumber() && op.getMulti())); + globalOpCounters.gotDelete(); auto& curOp = *CurOp::get(opCtx); { |