diff options
author | Siyuan Zhou <siyuan.zhou@mongodb.com> | 2019-01-21 19:11:41 -0500 |
---|---|---|
committer | Siyuan Zhou <siyuan.zhou@mongodb.com> | 2019-01-24 22:49:49 -0500 |
commit | d1933f0b1b88c4c2ad0f2fdd6ab106f7eeacddfa (patch) | |
tree | f28cbc8eb44fa4363c9824e0e772014c0306f433 /jstests | |
parent | ec4520d72b2a4ff6ba980e913c988b11e7d188a4 (diff) | |
download | mongo-d1933f0b1b88c4c2ad0f2fdd6ab106f7eeacddfa.tar.gz |
SERVER-38282 Yield locks for prepared transactions on stepdown.
Diffstat (limited to 'jstests')
-rw-r--r-- | jstests/core/txns/libs/prepare_helpers.js | 2 | ||||
-rw-r--r-- | jstests/replsets/prepared_transaction_on_failover.js | 91 |
2 files changed, 92 insertions, 1 deletions
diff --git a/jstests/core/txns/libs/prepare_helpers.js b/jstests/core/txns/libs/prepare_helpers.js index 56927af8b56..d157fc351ce 100644 --- a/jstests/core/txns/libs/prepare_helpers.js +++ b/jstests/core/txns/libs/prepare_helpers.js @@ -40,7 +40,7 @@ const PrepareHelpers = (function() { // End the transaction on the shell session. if (res.ok) { - session.commitTransaction(); + session.commitTransaction_forTesting(); } else { session.abortTransaction_forTesting(); } diff --git a/jstests/replsets/prepared_transaction_on_failover.js b/jstests/replsets/prepared_transaction_on_failover.js new file mode 100644 index 00000000000..faec21bc52b --- /dev/null +++ b/jstests/replsets/prepared_transaction_on_failover.js @@ -0,0 +1,91 @@ +/** + * Tests prepared transactions can survive failover and commit on a new primary. + * + * @tags: [uses_transactions, uses_prepare_transaction] + */ +(function() { + "use strict"; + load("jstests/core/txns/libs/prepare_helpers.js"); + load("jstests/replsets/rslib.js"); // For reconnect() + + const replTest = new ReplSetTest({nodes: 2}); + replTest.startSet(); + replTest.initiate(); + + const dbName = jsTest.name(); + const collName = "coll"; + const otherDbName = dbName + "_other"; + + function testTransactionsWithFailover(stepDownFunction) { + const primary = replTest.getPrimary(); + const newPrimary = replTest.getSecondary(); + const testDB = primary.getDB(dbName); + + testDB.dropDatabase(); + testDB.getSiblingDB(otherDbName).dropDatabase(); + assert.commandWorked(testDB.runCommand({create: collName, writeConcern: {w: "majority"}})); + + jsTestLog("Starting transaction"); + const session = primary.startSession({causalConsistency: false}); + const sessionDB = session.getDatabase(dbName); + session.startTransaction({writeConcern: {w: "majority"}}); + + const doc = {_id: "txn on primary " + primary}; + assert.commandWorked(sessionDB.getCollection(collName).insert(doc)); + + jsTestLog("Putting transaction into prepare"); + const prepareTimestamp = PrepareHelpers.prepareTransaction(session); + replTest.awaitReplication(); + + stepDownFunction(); + reconnect(primary); + + jsTestLog("Waiting for the other node to run for election and become primary"); + assert.eq(replTest.getPrimary(), newPrimary); + + jsTestLog("Creating an unrelated collection"); + // Application of an unrelated DDL command needs a strong lock on secondary. Make sure + // the prepared transactions have yielded their locks on secondary. + assert.commandWorked(newPrimary.getDB(otherDbName).runCommand({create: collName})); + replTest.awaitReplication(); + + jsTestLog("Dropping the collection in use cannot acquire the lock"); + assert.commandFailedWithCode( + newPrimary.getDB(testDB).runCommand({drop: collName, maxTimeMS: 1000}), + ErrorCodes.MaxTimeMSExpired); + + jsTestLog("Committing transaction on the new primary"); + // Create a proxy session to reuse the session state of the old primary. + const newSession = new _DelegatingDriverSession(newPrimary, session); + + assert.commandWorked( + PrepareHelpers.commitTransactionAfterPrepareTS(newSession, prepareTimestamp)); + replTest.awaitReplication(); + + assert.docEq(doc, testDB.getCollection(collName).findOne()); + assert.docEq(doc, newPrimary.getDB(dbName).getCollection(collName).findOne()); + + jsTestLog("Running another transaction on the new primary"); + const secondSession = newPrimary.startSession({causalConsistency: false}); + secondSession.startTransaction({writeConcern: {w: "majority"}}); + assert.commandWorked( + secondSession.getDatabase(dbName).getCollection(collName).insert({_id: "second-doc"})); + secondSession.commitTransaction(); + } + + function stepDownViaHeartbeat() { + jsTestLog("Stepping down primary via heartbeat"); + replTest.stepUp(replTest.getSecondary()); + } + testTransactionsWithFailover(stepDownViaHeartbeat); + + function stepDownViaCommand() { + jsTestLog("Stepping down primary via command"); + assert.throws(function() { + replTest.getPrimary().adminCommand({replSetStepDown: 10}); + }); + } + testTransactionsWithFailover(stepDownViaCommand); + + replTest.stopSet(); +})(); |