0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/jstests/replsets/prepared_transaction_on_failover.js
2019-07-27 11:02:23 -04:00

132 lines
5.1 KiB
JavaScript

/**
* 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(doWork, stepDown, postCommit) {
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});
session.startTransaction({writeConcern: {w: "majority"}});
doWork(primary, session);
jsTestLog("Putting transaction into prepare");
const prepareTimestamp = PrepareHelpers.prepareTransaction(session);
replTest.awaitReplication();
stepDown();
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.commitTransaction(newSession, prepareTimestamp));
replTest.awaitReplication();
postCommit(primary, newPrimary);
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"}));
assert.commandWorked(secondSession.commitTransaction_forTesting());
// Unfreeze the original primary so that it can stand for election again for the next test.
assert.commandWorked(primary.adminCommand({replSetFreeze: 0}));
}
function doInsert(primary, session) {
const doc = {_id: "txn on primary " + primary};
jsTestLog("Inserting a document in a transaction.");
assert.commandWorked(session.getDatabase(dbName).getCollection(collName).insert(doc));
}
function postInsert(primary, newPrimary) {
const doc = {_id: "txn on primary " + primary};
assert.docEq(doc, primary.getDB(dbName).getCollection(collName).findOne());
assert.docEq(doc, newPrimary.getDB(dbName).getCollection(collName).findOne());
}
function doInsertTextSearch(primary, session) {
// Create an index outside of the transaction.
assert.commandWorked(primary.getDB(dbName).getCollection(collName).createIndex({text: "text"}));
// Do the followings in a transaction.
jsTestLog("Inserting a document in a transaction.");
assert.commandWorked(
session.getDatabase(dbName).getCollection(collName).insert({text: "text"}));
// Text search will recursively acquire the global lock. This tests that yielding
// recursively held locks works on step down.
jsTestLog("Doing a text search in a transaction.");
assert.eq(1,
session.getDatabase(dbName)
.getCollection(collName)
.find({$text: {$search: "text"}})
.itcount());
}
function postInsertTextSearch(primary, newPrimary) {
assert.eq(
1,
primary.getDB(dbName).getCollection(collName).find({$text: {$search: "text"}}).itcount());
assert.eq(1,
newPrimary.getDB(dbName)
.getCollection(collName)
.find({$text: {$search: "text"}})
.itcount());
}
function stepDownViaHeartbeat() {
jsTestLog("Stepping down primary via heartbeat");
replTest.stepUp(replTest.getSecondary());
}
function stepDownViaCommand() {
jsTestLog("Stepping down primary via command");
assert.commandWorked(replTest.getPrimary().adminCommand({replSetStepDown: 10}));
}
testTransactionsWithFailover(doInsert, stepDownViaHeartbeat, postInsert);
testTransactionsWithFailover(doInsert, stepDownViaCommand, postInsert);
testTransactionsWithFailover(doInsertTextSearch, stepDownViaHeartbeat, postInsertTextSearch);
testTransactionsWithFailover(doInsertTextSearch, stepDownViaCommand, postInsertTextSearch);
replTest.stopSet();
})();