/** * This test shuts down a replica set during a two-phase index build. The test corrupts a WiredTiger * collection file and expects that --repair salvages the data and drops the unfinished index. * * @tags: [requires_wiredtiger, requires_replication] */ import { assertErrorOnStartupWhenStartingAsReplSet, assertRepairSucceeds, assertStartInReplSet, corruptFile, getUriForColl, startMongodOnExistingPath, } from "jstests/disk/libs/wt_file_helper.js"; import {ReplSetTest} from "jstests/libs/replsettest.js"; import {IndexBuildTest} from "jstests/noPassthrough/libs/index_builds/index_build.js"; const dbName = "repair_unfinished_indexes"; const collName = "test"; const replSet = new ReplSetTest({nodes: 2}); replSet.startSet(); replSet.initiate(null, null, {initiateWithDefaultElectionTimeout: true}); const primary = replSet.getPrimary(); const primaryDB = primary.getDB(dbName); const secondary = replSet.getSecondary(); const secondaryDB = secondary.getDB(dbName); const secondaryPort = secondary.port; const secondaryDbpath = secondary.dbpath; const primaryColl = primaryDB.getCollection(collName); assert.commandWorked(primaryColl.insert({_id: 0, a: 1})); jsTestLog("Starting index build on primary and pausing before completion"); IndexBuildTest.pauseIndexBuilds(primary); const createIdx = IndexBuildTest.startIndexBuild(primary, primaryColl.getFullName(), {a: 1}); jsTestLog("Waiting for secondary to start the index build"); IndexBuildTest.waitForIndexBuildToStart(secondaryDB); const secondaryCollUri = getUriForColl(secondaryDB[collName]); replSet.stop(secondary); // Confirm that the secondary node leaves the index as unfinished. (function startAsStandalone() { jsTestLog("Starting secondary as standalone"); const mongod = startMongodOnExistingPath( secondaryDbpath, // This parameter ensures that when the standalone starts up, it applies all unapplied oplog // entries since the last shutdown. This "smooths out" a race condition in this test where // the secondary can shut down without fully applying the 'startIndexBuild' oplog entry, and // not advancing the stable timestamp to the top of the oplog. {setParameter: "recoverFromOplogAsStandalone=true"}, ); IndexBuildTest.assertIndexes(mongod.getDB(dbName).getCollection(collName), 2, ["_id_"], ["a_1"], { includeBuildUUIDs: true, }); MongoRunner.stopMongod(mongod); })(); const exitCode = createIdx({checkExitSuccess: false}); assert.neq(0, exitCode, "expected shell to exit abnormally due to shutdown"); const secondaryCollFile = secondaryDbpath + "/" + secondaryCollUri + ".wt"; jsTestLog("Corrupting secondary collection file: " + secondaryCollFile); corruptFile(secondaryCollFile); assertRepairSucceeds(secondaryDbpath, secondaryPort); // Importantly, confirm that the secondary node dropped the unfinished index. (function startAsStandaloneAfterRepair() { jsTestLog("Starting secondary as standalone after repair"); const mongod = startMongodOnExistingPath(secondaryDbpath); IndexBuildTest.assertIndexes(mongod.getDB(dbName).getCollection(collName), 1, ["_id_"], [], { includeBuildUUIDs: true, }); MongoRunner.stopMongod(mongod); })(); // The secondary may not be reintroduced because data was modified. assertErrorOnStartupWhenStartingAsReplSet(secondaryDbpath, secondaryPort, replSet.getReplSetConfig()._id); (function reSyncSecondary() { jsTestLog("Wiping dbpath and re-syncing secondary"); const newSecondary = assertStartInReplSet( replSet, secondary, true /* cleanData */, true /* expectResync */, function (node) {}, ); IndexBuildTest.resumeIndexBuilds(primary); IndexBuildTest.waitForIndexBuildToStop(primaryDB); replSet.awaitReplication(); IndexBuildTest.assertIndexes(primaryColl, 2, ["_id_", "a_1"]); IndexBuildTest.assertIndexes(newSecondary.getDB(dbName).getCollection(collName), 2, ["_id_", "a_1"]); })(); replSet.stopSet();