Files
mongo/jstests/sharding/txn_two_phase_commit_primary_exit_drain.js
Zac 591928c619 SERVER-108478 JS formatted by prettier and remove clang-format (#39656)
GitOrigin-RevId: 6c8f6aded47f260aa4f7c231b17dae3302cb1e04
2025-08-21 17:27:09 +00:00

115 lines
4.1 KiB
JavaScript

/**
* Exercises the coordinator commands logic to test if a primary exits drain mode after the primary
* steps down during a basic two phase commit.
*
* @tags: [uses_transactions, uses_prepare_transaction, uses_multi_shard_transaction,
* multiversion_incompatible]
*/
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {ShardingTest} from "jstests/libs/shardingtest.js";
import {
checkDecisionIs,
checkDocumentDeleted,
runCommitThroughMongosInParallelThread,
} from "jstests/sharding/libs/txn_two_phase_commit_util.js";
const dbName = "test";
const collName = "foo";
const ns = dbName + "." + collName;
let st = new ShardingTest({shards: 3, causallyConsistent: true});
let coordinator = st.shard0;
let participant1 = st.shard1;
let participant2 = st.shard2;
let lsid = {id: UUID()};
let txnNumber = 0;
const setUp = function () {
// Create a sharded collection with a chunk on each shard:
// shard0: [-inf, 0)
// shard1: [0, 10)
// shard2: [10, +inf)
assert.commandWorked(st.s.adminCommand({enableSharding: dbName, primaryShard: coordinator.shardName}));
assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}}));
assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 0}}));
assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 10}}));
assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: 0}, to: participant1.shardName}));
assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: 10}, to: participant2.shardName}));
// Start a new transaction by inserting a document onto each shard.
assert.commandWorked(
st.s.getDB(dbName).runCommand({
insert: collName,
documents: [{_id: -5}, {_id: 5}, {_id: 15}],
lsid: lsid,
txnNumber: NumberLong(txnNumber),
stmtId: NumberInt(0),
startTransaction: true,
autocommit: false,
}),
);
};
const testCommitProtocol = function () {
jsTest.log("Testing two-phase commit");
txnNumber++;
setUp();
const coordinatorPrimary = coordinator.rs.getPrimary();
const hangBeforeWaitingForDecisionWriteConcernFp = configureFailPoint(
coordinatorPrimary,
"hangBeforeWaitingForDecisionWriteConcern",
{},
"alwaysOn",
);
const commitThread = runCommitThroughMongosInParallelThread(lsid, txnNumber, st.s.host);
commitThread.start();
// Check that the coordinator wrote the decision.
hangBeforeWaitingForDecisionWriteConcernFp.wait();
const commitTimestamp = checkDecisionIs(coordinator, lsid, txnNumber, "commit");
assert.commandWorked(coordinatorPrimary.adminCommand({replSetStepDown: ReplSetTest.kForeverSecs, force: true}));
// The replSetFreeze command will cause the node to run for primary on its own.
assert.commandWorked(coordinatorPrimary.adminCommand({replSetFreeze: 0}));
// The primary won't be able to exit drain mode while the
// hangBeforeWaitingForDecisionWriteConcern failpoint is active.
coordinator.rs.waitForState(coordinatorPrimary, ReplSetTest.State.PRIMARY);
const helloRes = assert.commandWorked(coordinatorPrimary.adminCommand("hello"));
assert.eq(false, helloRes.isWritablePrimary, helloRes);
hangBeforeWaitingForDecisionWriteConcernFp.off();
// However after the hangBeforeWaitingForDecisionWriteConcern failpoint is disabled the primary
// is expected to exit drain mode.
commitThread.join();
// Check that the coordinator deleted its persisted state.
assert.soon(function () {
return checkDocumentDeleted(coordinator, lsid, txnNumber);
});
// Check that the transaction committed as expected.
jsTest.log("Verify that the transaction was committed on all shards.");
const res = assert.commandWorked(
st.s.getDB(dbName).runCommand({
find: collName,
readConcern: {level: "majority", afterClusterTime: commitTimestamp},
maxTimeMS: 10000,
}),
);
assert.eq(3, res.cursor.firstBatch.length);
};
testCommitProtocol();
st.stop();