Files
mongo/jstests/replsets/reconstruct_prepared_transactions_initial_sync_index_build.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

138 lines
5.2 KiB
JavaScript

/**
* Tests that initial sync successfully applies a prepare oplog entry during oplog application phase
* of initial sync. Additionally, we will test that a background index build interleaves without
* hanging.
*
* @tags: [
* uses_prepare_transaction,
* uses_transactions,
* ]
*/
import {PrepareHelpers} from "jstests/core/txns/libs/prepare_helpers.js";
import {kDefaultWaitForFailPointTimeout} from "jstests/libs/fail_point_util.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {IndexBuildTest} from "jstests/noPassthrough/libs/index_builds/index_build.js";
const replTest = new ReplSetTest({nodes: 2});
replTest.startSet();
const config = replTest.getReplSetConfig();
// Increase the election timeout so that we do not accidentally trigger an election while the
// secondary is restarting.
config.settings = {
"electionTimeoutMillis": 12 * 60 * 60 * 1000,
};
replTest.initiate(config);
const primary = replTest.getPrimary();
let secondary = replTest.getSecondary();
// The default WC is majority and this test can't satisfy majority writes.
assert.commandWorked(
primary.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}),
);
const dbName = "test";
const collName = "reconstruct_prepared_transactions_initial_sync_index_build";
let testDB = primary.getDB(dbName);
let testColl = testDB.getCollection(collName);
assert.commandWorked(testColl.insert({_id: 0}));
jsTestLog("Restarting the secondary");
// Restart the secondary with startClean set to true so that it goes through initial sync. Also
// restart the node with a failpoint turned on that will pause initial sync. This way we can do
// some writes on the sync source while initial sync is paused and know that its operations
// won't be copied during collection cloning. Instead, the writes must be applied during oplog
// application.
replTest.stop(secondary, undefined /* signal */, {skipValidation: true});
secondary = replTest.start(
secondary,
{
startClean: true,
setParameter: {
"failpoint.initialSyncHangDuringCollectionClone": tojson({
mode: "alwaysOn",
data: {namespace: testColl.getFullName(), numDocsToClone: 1},
}),
"numInitialSyncAttempts": 1,
},
},
true /* wait */,
);
// Wait for failpoint to be reached so we know that collection cloning is paused.
assert.commandWorked(
secondary.adminCommand({
waitForFailPoint: "initialSyncHangDuringCollectionClone",
timesEntered: 1,
maxTimeMS: kDefaultWaitForFailPointTimeout,
}),
);
jsTestLog("Running operations while collection cloning is paused");
// Perform writes while collection cloning is paused so that we know they must be applied during
// the oplog application stage of initial sync.
assert.commandWorked(testColl.insert({_id: 1, a: 1}));
// Make the index build hang on the primary so that only a startIndexBuild oplog entry is
// replicated and initial sync gets to the prepared-txn reconstruct stage with the index build
// still running.
jsTest.log("Hanging index build on the primary node");
IndexBuildTest.pauseIndexBuilds(primary);
jsTest.log("Beginning index build");
let awaitIndexBuild = IndexBuildTest.startIndexBuild(primary, testColl.getFullName(), {a: 1});
let session = primary.startSession();
let sessionDB = session.getDatabase(dbName);
const sessionColl = sessionDB.getCollection(collName);
jsTestLog("Preparing the transaction");
// Prepare a transaction while collection cloning is paused so that its oplog entry must be
// applied during the oplog application phase of initial sync.
session.startTransaction();
assert.commandWorked(sessionColl.update({_id: 1, a: 1}, {_id: 1, a: 2}));
const prepareTimestamp = PrepareHelpers.prepareTransaction(session, {w: 1});
jsTestLog("Resuming initial sync");
// Resume initial sync.
assert.commandWorked(secondary.adminCommand({configureFailPoint: "initialSyncHangDuringCollectionClone", mode: "off"}));
// Unblock index build.
// Let the primary finish its index build and replicate a commit to the secondary.
IndexBuildTest.resumeIndexBuilds(primary);
// Wait for the secondary to complete initial sync.
replTest.awaitSecondaryNodes();
jsTestLog("Initial sync completed");
secondary.setSecondaryOk();
const secondaryColl = secondary.getDB(dbName).getCollection(collName);
// Make sure that while reading from the node that went through initial sync, we can't read
// changes to the documents from the prepared transaction after initial sync. Also, make
// sure that the writes that happened when collection cloning was paused happened.
const res = secondaryColl.find().sort({_id: 1}).toArray();
assert.eq(res, [{_id: 0}, {_id: 1, a: 1}], res);
// Wait for the prepared transaction oplog entry to be majority committed before committing the
// transaction.
PrepareHelpers.awaitMajorityCommitted(replTest, prepareTimestamp);
jsTestLog("Committing the transaction");
assert.commandWorked(PrepareHelpers.commitTransaction(session, prepareTimestamp));
replTest.awaitReplication();
awaitIndexBuild();
// Make sure that we can see the data from the committed transaction on the secondary.
assert.docEq({_id: 1, a: 2}, secondaryColl.findOne({_id: 1}));
replTest.stopSet();