/** * Tests that retryable findAndModify data stored in the `config.image_collection` side collection * do not get populated by nodes doing oplog application while in initial sync. * * This setParameter behavior does not yet exist on earlier versions. * @tags: [multiversion_incompatible] */ import {ReplSetTest} from "jstests/libs/replsettest.js"; // A secondary cannot compute retryable images during initial sync. Thus we skip db hash checks as // its expected for config.image_collection to not match. TestData.skipCheckDBHashes = true; // Start a single node replica set. const rst = new ReplSetTest({nodes: 1}); rst.startSet(); rst.initiate(); const dbName = jsTest.name(); const primary = rst.getPrimary(); const primaryDB = primary.getDB(dbName); const primaryColl = primaryDB["collection"]; primaryColl.insert({_id: 1}); let lsid = UUID(); jsTestLog({ "Pre-initial sync retryable findAndModify": assert.commandWorked( primaryDB.runCommand({ findandmodify: primaryColl.getName(), lsid: {id: lsid}, txnNumber: NumberLong(1), stmtId: NumberInt(1), query: {_id: 1}, new: false, update: {$set: {preInitialSync: true}}, }), ), }); jsTestLog("Adding a new voting node (node1) to the replica set."); const node1 = rst.add({ rsConfig: {priority: 1, votes: 1}, setParameter: {"failpoint.initialSyncHangAfterDataCloning": tojson({mode: "alwaysOn"})}, }); rst.reInitiate(); jsTestLog("Wait for node1 to hang during initial sync."); checkLog.containsJson(node1, 21184); // Perform a findAndModify update that will not be retryable on the node that's concurrently initial // syncing. let result = assert.commandWorked( primaryDB.runCommand({ findandmodify: primaryColl.getName(), lsid: {id: lsid}, txnNumber: NumberLong(2), stmtId: NumberInt(1), query: {_id: 1}, new: false, update: {$set: {duringInitialSync: true}}, }), ); jsTestLog({"Retryable findAndModify during initial sync": result}); // With a separate logical session, perform a findAndModify removal that will not be retryable on // the node that's concurrently initial syncing. let otherLsid = UUID(); jsTestLog({ "Retryable findAndModify removal during initial sync": assert.commandWorked( primaryDB.runCommand({ findandmodify: primaryColl.getName(), lsid: {id: otherLsid}, txnNumber: NumberLong(3), stmtId: NumberInt(1), query: {_id: 1}, new: false, remove: true, }), ), }); jsTestLog("Resuming initial sync."); assert.commandWorked(node1.adminCommand({configureFailPoint: "initialSyncHangAfterDataCloning", mode: "off"})); rst.awaitSecondaryNodes(null, [node1]); let initialSyncedNode = rst.getSecondary(); rst.stepUp(initialSyncedNode); result = assert.commandFailedWithCode( initialSyncedNode.getDB(dbName).runCommand({ findandmodify: primaryColl.getName(), lsid: {id: lsid}, txnNumber: NumberLong(2), stmtId: NumberInt(1), query: {_id: 1}, new: false, update: {$set: {duringInitialSync: true}}, }), ErrorCodes.IncompleteTransactionHistory, ); // Assert that retrying the update fails. jsTestLog({ "Secondary": initialSyncedNode, "Data": initialSyncedNode.getDB(dbName)["collection"].findOne(), "Image": initialSyncedNode.getDB("config")["image_collection"].findOne({"_id.id": lsid}), "retried findAndModify against synced node": result, }); result = assert.commandFailedWithCode( initialSyncedNode.getDB(dbName).runCommand({ findandmodify: primaryColl.getName(), lsid: {id: otherLsid}, txnNumber: NumberLong(3), stmtId: NumberInt(1), query: {_id: 1}, new: false, remove: true, }), ErrorCodes.IncompleteTransactionHistory, ); // Assert that retrying the delete fails. jsTestLog({ "Secondary": initialSyncedNode, "Data": initialSyncedNode.getDB(dbName)["collection"].findOne(), "Image": initialSyncedNode.getDB("config")["image_collection"].findOne({"_id.id": otherLsid}), "retried delete against synced node": result, }); // There should be two sessions/image entries on the initial syncing node, and both should be // flagged as invalidated. assert.eq(2, initialSyncedNode.getDB("config")["image_collection"].count({invalidated: true})); assert.eq(2, initialSyncedNode.getDB("config")["image_collection"].count({invalidatedReason: "initial sync"})); rst.stopSet();