/** * Tests missing index keys check in dbCheck with ranges specified. Primary and secondary will have * index inconsistencies for different docs, and dbCheck will miss detecting these inconsistencies * if the doc is outside of the range specified in the dbCheck invocation. * * @tags: [ * featureFlagSecondaryIndexChecksInDbCheck * ] */ import {configureFailPoint} from "jstests/libs/fail_point_util.js"; import {ReplSetTest} from "jstests/libs/replsettest.js"; import {checkHealthLog, clearHealthLog, runDbCheck} from "jstests/replsets/libs/dbcheck_utils.js"; // Skipping data consistency checks because data is inserted into primary and secondary separately. TestData.skipCollectionAndIndexValidation = true; TestData.skipCheckDBHashes = true; const rst = new ReplSetTest({name: jsTestName(), nodes: 2}); rst.startSet(); rst.initiate(); const dbName = "dbCheckRangeMissingKeys"; const collName = "testColl"; const primary = rst.getPrimary(); const primaryDb = primary.getDB(dbName); const secondary = rst.getSecondary(); const secondaryDb = secondary.getDB(dbName); const primaryHealthLog = primary.getDB("local").system.healthlog; const secondaryHealthLog = secondary.getDB("local").system.healthlog; const errQuery = { operation: "dbCheckBatch", severity: "error", }; const missingKeyErrQuery = { ...errQuery, msg: "Document has missing index keys", }; function runTestWithCollOpts(collOpts) { jsTestLog("Running dbcheck missing key range check with collection options: " + tojson(collOpts)); primaryDb[collName].drop(); assert.commandWorked(primaryDb.createCollection(collName, collOpts)); assert.commandWorked(primaryDb.runCommand({ createIndexes: collName, indexes: [{key: {a: 1}, name: 'a_1'}], })); rst.awaitReplication(); // Insert docs with _id from 0 to 100. The doc with _id: 25 will be missing index keys on the // primary and the doc with _id: 75 will be missing index keys on the secondary. const numDocs = 100; let skipIndexingFp; for (let i = 0; i < numDocs; i++) { if (i === 25) { // Skip indexing the doc with _id: 25 on the primary. skipIndexingFp = configureFailPoint(primaryDb, "skipIndexNewRecords", {skipIdIndex: false}); } else if (i === 75) { // Skip indexing the doc with _id: 75 on the primary. skipIndexingFp = configureFailPoint(secondaryDb, "skipIndexNewRecords", {skipIdIndex: false}); } assert.commandWorked(primaryDb[collName].insert({_id: i, a: i})); // We need to wait the insert to be applied on secondary to make sure there is exactly one // index skipped. rst.awaitReplication(); if (skipIndexingFp) { skipIndexingFp.off(); skipIndexingFp = null; } } rst.awaitReplication(); function runTest(start, end, numExpectedErrorsPrimary, numExpectedErrorsSecondary) { // Run dbCheck on the specified range. runDbCheck(rst, primaryDb, collName, { maxDocsPerBatch: 10, validateMode: "dataConsistencyAndMissingIndexKeysCheck", start: {_id: start}, end: {_id: end}, }); // Inconsistency detected on primary but not secondary. checkHealthLog(primaryHealthLog, errQuery, numExpectedErrorsPrimary); checkHealthLog(primaryHealthLog, missingKeyErrQuery, numExpectedErrorsPrimary); checkHealthLog(secondaryHealthLog, errQuery, numExpectedErrorsSecondary); checkHealthLog(secondaryHealthLog, missingKeyErrQuery, numExpectedErrorsSecondary); clearHealthLog(rst); } jsTestLog("Run dbCheck on the full range"); runTest(0, 100, 1 /* numExpectedErrorsPrimary */, 1 /* numExpectedErrorsSecondary */); jsTestLog("Run dbCheck on a range without any inconsistencies."); runTest(0, 24, 0 /* numExpectedErrorsPrimary */, 0 /* numExpectedErrorsSecondary */); jsTestLog("Run dbCheck on a range that would detect only the primary inconsistency."); runTest(0, 50, 1 /* numExpectedErrorsPrimary */, 0 /* numExpectedErrorsSecondary */); jsTestLog("Run dbCheck on a range that would detect only the secondary inconsistency."); runTest(50, 100, 0 /* numExpectedErrorsPrimary */, 1 /* numExpectedErrorsSecondary */); jsTestLog( "Run dbCheck with the range specified starting/ending with the missing index key on primary"); runTest(24, 25, 1 /* numExpectedErrorsPrimary */, 0 /* numExpectedErrorsSecondary */); jsTestLog( "Run dbCheck with the range specified starting/ending with the missing index key on secondary"); runTest(74, 75, 0 /* numExpectedErrorsPrimary */, 1 /* numExpectedErrorsSecondary */); jsTestLog("Run dbCheck when the start/end range does not exist in the collection"); runTest(-1, 50, 1 /* numExpectedErrorsPrimary */, 0 /* numExpectedErrorsSecondary */); runTest(50, 101, 0 /* numExpectedErrorsPrimary */, 1 /* numExpectedErrorsSecondary */); runTest(101, 201, 0 /* numExpectedErrorsPrimary */, 0 /* numExpectedErrorsSecondary */); } [{}, {clusteredIndex: {key: {_id: 1}, unique: true}}] .forEach(collOpts => { runTestWithCollOpts(collOpts); }); rst.stopSet();