mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
132 lines
5.1 KiB
JavaScript
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();
|
|
})();
|