mongo/jstests/replsets/stepdown_race_with_transact...

76 lines
2.5 KiB
JavaScript

/**
* Tests that multi-documment transactions no longer race with stepdown over
* "setAlwaysInterruptAtStepDownOrUp".
* @tags: [uses_transactions]
*/
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
const rst = new ReplSetTest({nodes: 1});
rst.startSet();
rst.initiate();
const dbName = "testdb";
const collName = "testcoll";
const primary = rst.getPrimary();
const primaryDB = primary.getDB(dbName);
const primaryColl = primaryDB.getCollection(collName);
// Insert a document that we will later modify in a transaction.
assert.commandWorked(primaryColl.insert({a: 1}));
// In the first part of the race, we set the 'setAlwaysInterruptAtStepDownOrUp' too late,
// after stepDown is already done interrupting operations.
const txnFpBefore = configureFailPoint(primary, "hangBeforeSettingTxnInterruptFlag");
// The second critical part of the race is when the transaction thread has already passed
// the regular "not primary" checks by the time stepDown has completed and updated
// writability. (This is the reason we check writability again in the accompanying patch.)
const txnFpAfter = configureFailPoint(primary, "hangAfterCheckingWritabilityForMultiDocumentTransactions");
jsTestLog("Start the transaction in a parallel shell");
const txn = startParallelShell(() => {
const session = db.getMongo().startSession();
const sessionDB = session.getDatabase("testdb");
const sessionColl = sessionDB.getCollection("testcoll");
session.startTransaction();
assert.commandFailedWithCode(sessionColl.insert({b: 2}), ErrorCodes.NotWritablePrimary);
}, primary.port);
jsTestLog("Wait on the first transaction fail point");
txnFpBefore.wait();
const stepdownFP = configureFailPoint(primary, "stepdownHangAfterGrabbingRSTL");
jsTestLog("Issue a stepdown in a parallel shell");
const stepdown = startParallelShell(() => {
assert.commandWorked(db.adminCommand({replSetStepDown: 10 * 60, force: true}));
}, primary.port);
jsTestLog("Wait on the stepdown fail point");
stepdownFP.wait();
// The txn will be forced to wait for stepdown to finish.
jsTestLog("Release the first transaction fail point and wait in the second");
txnFpBefore.off();
txnFpAfter.wait();
jsTestLog("Let stepdown finish");
stepdownFP.off();
stepdown();
jsTestLog("Wait on the second transaction fail point");
txnFpAfter.wait();
jsTestLog("Let the transaction attempt finish");
txnFpAfter.off();
txn();
jsTestLog("Checking that the transaction never succeeded");
assert.eq(1, primaryColl.find().toArray().length);
rst.stopSet();