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

96 lines
3.6 KiB
JavaScript

/**
* Tests that reading at an atClusterTime earlier than the timestamp when initial sync finishes
* would result in SnapshotTooOld error regardless of the snapshot history window.
*
* @tags: [
* requires_majority_read_concern,
* requires_persistence,
* ]
*/
import {kDefaultWaitForFailPointTimeout} from "jstests/libs/fail_point_util.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
const replSet = new ReplSetTest({nodes: 2});
replSet.startSet();
replSet.initiate();
const collName = "coll";
const primary = replSet.getPrimary();
const secondary = replSet.getSecondary();
const primaryDB = primary.getDB("test");
const secondaryDB = secondary.getDB("test");
assert.commandWorked(primaryDB.runCommand({insert: collName, documents: [{_id: 0}], writeConcern: {w: "majority"}}));
jsTestLog("Adding a new node");
const newNode = replSet.add({
rsConfig: {priority: 0},
setParameter: {
"failpoint.forceSyncSourceCandidate": tojson({mode: "alwaysOn", data: {"hostAndPort": primary.host}}),
"failpoint.initialSyncHangAfterDataCloning": tojson({mode: "alwaysOn"}),
"numInitialSyncAttempts": 1,
// Set a large snapshot history window of 1 hour.
"minSnapshotHistoryWindowInSeconds": 3600,
},
});
replSet.reInitiate();
replSet.waitForState(newNode, ReplSetTest.State.STARTUP_2);
const newNodeDB = newNode.getDB("test");
assert.commandWorked(
newNode.adminCommand({
waitForFailPoint: "initialSyncHangAfterDataCloning",
timesEntered: 1,
maxTimeMS: kDefaultWaitForFailPointTimeout,
}),
);
// Perform another write after the new node finishes cloning.
const timestampDuringInitialSync = assert.commandWorked(
primaryDB.runCommand({insert: collName, documents: [{_id: 1}], writeConcern: {w: "majority"}}),
).operationTime;
jsTestLog("timestampDuringInitialSync is " + tojson(timestampDuringInitialSync));
// Perform snapshot reads on both the primary and the secondary and test that we can see the
// majority committed writes.
const findAtClusterTimeDuringInitialSync = {
find: collName,
readConcern: {level: "snapshot", atClusterTime: timestampDuringInitialSync},
};
const documents = [{_id: 0}, {_id: 1}];
assert.sameMembers(primaryDB.runCommand(findAtClusterTimeDuringInitialSync).cursor.firstBatch, documents);
assert.sameMembers(secondaryDB.runCommand(findAtClusterTimeDuringInitialSync).cursor.firstBatch, documents);
// Test reading at a timestamp before initial sync finishes is not allowed while the node is in
// initial sync.
assert.commandFailedWithCode(
newNodeDB.runCommand(findAtClusterTimeDuringInitialSync),
ErrorCodes.NotPrimaryOrSecondary,
);
// Perform another write so that the new node will finish initial sync at a timestamp higher than
// the timestampDuringInitialSync.
assert.commandWorked(primaryDB.runCommand({insert: collName, documents: [{_id: 2}]}));
// Allow the new node to complete initial sync.
assert.commandWorked(newNode.adminCommand({configureFailPoint: "initialSyncHangAfterDataCloning", mode: "off"}));
replSet.awaitSecondaryNodes(null, [newNode]);
replSet.awaitLastOpCommitted();
// Test reading at a timestamp before initial sync finishes is not allowed even if the node has
// finished initial sync and has a large snapshot history window size.
assert.commandFailedWithCode(newNodeDB.runCommand(findAtClusterTimeDuringInitialSync), ErrorCodes.SnapshotTooOld);
// Test snapshot readConcern reads all committed writes.
assert.sameMembers(
newNodeDB.runCommand({
find: collName,
readConcern: {level: "snapshot"},
}).cursor.firstBatch,
[{_id: 0}, {_id: 1}, {_id: 2}],
);
replSet.stopSet();