diff options
19 files changed, 199 insertions, 133 deletions
diff --git a/jstests/concurrency/fsm_libs/cluster.js b/jstests/concurrency/fsm_libs/cluster.js index e7795dcd984..62ae4849682 100644 --- a/jstests/concurrency/fsm_libs/cluster.js +++ b/jstests/concurrency/fsm_libs/cluster.js @@ -521,8 +521,15 @@ var Cluster = function(options) { return; } + const validateOptions = {full: true, enforceFastCount: true}; + // TODO (SERVER-24266): Once fast counts are tolerant to unclean shutdowns, remove + // the check for TestData.allowUncleanShutdowns. + if (TestData.skipEnforceFastCountOnValidate || TestData.allowUncleanShutdowns) { + validateOptions.enforceFastCount = false; + } + assert.commandWorked( - validateCollections(db.getSiblingDB(dbInfo.name), {full: true}), + validateCollections(db.getSiblingDB(dbInfo.name), validateOptions), phase + ' collection validation failed'); }); }; diff --git a/jstests/libs/override_methods/validate_collections_on_shutdown.js b/jstests/libs/override_methods/validate_collections_on_shutdown.js index a2dafb1560d..ea0f1d4d3d0 100644 --- a/jstests/libs/override_methods/validate_collections_on_shutdown.js +++ b/jstests/libs/override_methods/validate_collections_on_shutdown.js @@ -124,7 +124,14 @@ MongoRunner.validateCollectionsCallback = function(port) { for (let i = 0; i < dbNames.length; ++i) { const dbName = dbNames[i]; cmds.then("validating " + dbName, function(conn) { - const validate_res = validateCollections(conn.getDB(dbName), {full: true}); + const validateOptions = {full: true, enforceFastCount: true}; + // TODO (SERVER-24266): Once fast counts are tolerant to unclean shutdowns, remove the + // check for TestData.allowUncleanShutdowns. + if (TestData.skipEnforceFastCountOnValidate || TestData.allowUncleanShutdowns) { + validateOptions.enforceFastCount = false; + } + + const validate_res = validateCollections(conn.getDB(dbName), validateOptions); if (!validate_res.ok) { return { shouldStop: true, diff --git a/jstests/replsets/prepare_conflict_read_concern_behavior.js b/jstests/replsets/prepare_conflict_read_concern_behavior.js index 4ad65f75506..1c36a75f11c 100644 --- a/jstests/replsets/prepare_conflict_read_concern_behavior.js +++ b/jstests/replsets/prepare_conflict_read_concern_behavior.js @@ -26,6 +26,10 @@ "use strict"; load("jstests/core/txns/libs/prepare_helpers.js"); +// This test completes with a prepared transaction still active, so we cannot enforce an accurate +// fast count. +TestData.skipEnforceFastCountOnValidate = true; + const replTest = new ReplSetTest({nodes: 2}); replTest.startSet(); replTest.initiate(); diff --git a/jstests/replsets/retrying_prepared_transaction_does_not_block_stepdown.js b/jstests/replsets/retrying_prepared_transaction_does_not_block_stepdown.js index 0eedcd3550e..b39c041e7d5 100644 --- a/jstests/replsets/retrying_prepared_transaction_does_not_block_stepdown.js +++ b/jstests/replsets/retrying_prepared_transaction_does_not_block_stepdown.js @@ -6,6 +6,11 @@ (function() { "use strict"; load("jstests/core/txns/libs/prepare_helpers.js"); + +// This test completes with a prepared transaction still active, so we cannot enforce an accurate +// fast count. +TestData.skipEnforceFastCountOnValidate = true; + const replTest = new ReplSetTest({nodes: 1}); replTest.startSet(); replTest.initiate(); diff --git a/jstests/replsets/rollback_transaction_table.js b/jstests/replsets/rollback_transaction_table.js index 3c1a18b436e..cf3b8bd98f3 100644 --- a/jstests/replsets/rollback_transaction_table.js +++ b/jstests/replsets/rollback_transaction_table.js @@ -23,6 +23,9 @@ // also manually simulates a session, which is not compatible with implicit sessions. TestData.disableImplicitSessions = true; +// TODO (SERVER-24266): Once fast counts are tolerant to rollbacks, remove this guard. +TestData.skipEnforceFastCountOnValidate = true; + load("jstests/libs/retryable_writes_util.js"); if (!RetryableWritesUtil.storageEngineSupportsRetryableWrites(jsTest.options().storageEngine)) { diff --git a/src/mongo/db/catalog/collection_validation.cpp b/src/mongo/db/catalog/collection_validation.cpp index 79baffce7f5..d4b23db541a 100644 --- a/src/mongo/db/catalog/collection_validation.cpp +++ b/src/mongo/db/catalog/collection_validation.cpp @@ -422,18 +422,15 @@ void _validateCatalogEntry(OperationContext* opCtx, Status validate(OperationContext* opCtx, const NamespaceString& nss, - ValidateOptions options, - bool background, + ValidateMode mode, ValidateResults* results, BSONObjBuilder* output, bool turnOnExtraLoggingForTest) { invariant(!opCtx->lockState()->isLocked() || storageGlobalParams.repair); - // Background validation does not support any type of full validation. - invariant(!(background && (options != ValidateOptions::kNoFullValidation))); // This is deliberately outside of the try-catch block, so that any errors thrown in the // constructor fail the cmd, as opposed to returning OK with valid:false. - ValidateState validateState(opCtx, nss, background, options, turnOnExtraLoggingForTest); + ValidateState validateState(opCtx, nss, mode, turnOnExtraLoggingForTest); const auto replCoord = repl::ReplicationCoordinator::get(opCtx); // Check whether we are allowed to read from this node after acquiring our locks. If we are @@ -448,14 +445,14 @@ Status validate(OperationContext* opCtx, // Full record store validation code is executed before we open cursors because it may close // and/or invalidate all open cursors. - if (options & ValidateOptions::kFullRecordStoreValidation) { + if (validateState.isFullValidation()) { invariant(opCtx->lockState()->isCollectionLockedForMode(validateState.nss(), MODE_X)); // For full record store validation we use the storage engine's validation // functionality. validateState.getCollection()->getRecordStore()->validate(opCtx, results, output); } - if (options & ValidateOptions::kFullIndexValidation) { + if (validateState.isFullIndexValidation()) { invariant(opCtx->lockState()->isCollectionLockedForMode(validateState.nss(), MODE_X)); // For full index validation, we validate the internal structure of each index and save // the number of keys in the index to compare against _validateIndexes()'s count @@ -590,7 +587,7 @@ Status validate(OperationContext* opCtx, output->append("ns", validateState.nss().ns()); } catch (ExceptionFor<ErrorCodes::CursorNotFound>&) { - invariant(background); + invariant(validateState.isBackground()); string warning = str::stream() << "Collection validation with {background: true} validates" << " the latest checkpoint (data in a snapshot written to disk in a consistent" diff --git a/src/mongo/db/catalog/collection_validation.h b/src/mongo/db/catalog/collection_validation.h index fa1b9986589..bd0e68a9dd3 100644 --- a/src/mongo/db/catalog/collection_validation.h +++ b/src/mongo/db/catalog/collection_validation.h @@ -41,23 +41,26 @@ class Status; namespace CollectionValidation { -enum class ValidateOptions { - kNoFullValidation = 0, +enum class ValidateMode { + kBackground, + kForeground, - // If the FullRecordStoreValidation option is set, validate() will do a full validation of the - // underlying record store using the storage engine's validation functionality. For WiredTiger - // this results in a call to verify(). - kFullRecordStoreValidation = 1 << 0, - // If set, validate() will validate the internal structure of each index, and checks consistency - // of the number of keys in the index compared to the internal structure. - kFullIndexValidation = 1 << 1, - // Includes all of the full validations above. - kFullValidation = kFullRecordStoreValidation | kFullIndexValidation, -}; + // The standard foreground validation above, plus a full validation of the underlying + // SortedDataInterface using the storage engine's validation functionality. For WiredTiger, + // this results in a call to verify() for each index. + // + // This mode is only used by repair to avoid revalidating the record store. + kForegroundFullIndexOnly, + + // The full index validation above, plus a full validation of the underlying record store using + // the storage engine's validation functionality. For WiredTiger, this results in a call to + // verify(). + kForegroundFull, -inline bool operator&(ValidateOptions lhs, ValidateOptions rhs) { - return (static_cast<int>(lhs) & static_cast<int>(rhs)) != 0; -} + // The full index and record store validation above, plus enforce that the fast count is equal + // to the number of records (as opposed to correcting the fast count if it is incorrect). + kForegroundFullEnforceFastCount, +}; /** * Expects the caller to hold no locks. @@ -72,8 +75,7 @@ inline bool operator&(ValidateOptions lhs, ValidateOptions rhs) { */ Status validate(OperationContext* opCtx, const NamespaceString& nss, - ValidateOptions options, - bool background, + ValidateMode mode, ValidateResults* results, BSONObjBuilder* output, bool turnOnExtraLoggingForTest = false); diff --git a/src/mongo/db/catalog/collection_validation_test.cpp b/src/mongo/db/catalog/collection_validation_test.cpp index 427fe02e746..b3ed9f5185f 100644 --- a/src/mongo/db/catalog/collection_validation_test.cpp +++ b/src/mongo/db/catalog/collection_validation_test.cpp @@ -91,18 +91,18 @@ public: * Calls validate on collection kNss with both kValidateFull and kValidateNormal validation levels * and verifies the results. */ -void foregroundValidate( - OperationContext* opCtx, bool valid, int numRecords, int numInvalidDocuments, int numErrors) { - std::vector<CollectionValidation::ValidateOptions> optionsList = { - CollectionValidation::ValidateOptions::kNoFullValidation, - CollectionValidation::ValidateOptions::kFullRecordStoreValidation, - CollectionValidation::ValidateOptions::kFullIndexValidation, - CollectionValidation::ValidateOptions::kFullValidation}; - for (auto options : optionsList) { +void foregroundValidate(OperationContext* opCtx, + bool valid, + int numRecords, + int numInvalidDocuments, + int numErrors, + std::initializer_list<CollectionValidation::ValidateMode> modes = { + CollectionValidation::ValidateMode::kForeground, + CollectionValidation::ValidateMode::kForegroundFull}) { + for (auto mode : modes) { ValidateResults validateResults; BSONObjBuilder output; - ASSERT_OK(CollectionValidation::validate( - opCtx, kNss, options, /*background*/ false, &validateResults, &output)); + ASSERT_OK(CollectionValidation::validate(opCtx, kNss, mode, &validateResults, &output)); ASSERT_EQ(validateResults.valid, valid); ASSERT_EQ(validateResults.errors.size(), static_cast<long unsigned int>(numErrors)); @@ -134,13 +134,8 @@ void backgroundValidate(OperationContext* opCtx, ValidateResults validateResults; BSONObjBuilder output; - ASSERT_OK( - CollectionValidation::validate(opCtx, - kNss, - CollectionValidation::ValidateOptions::kNoFullValidation, - /*background*/ true, - &validateResults, - &output)); + ASSERT_OK(CollectionValidation::validate( + opCtx, kNss, CollectionValidation::ValidateMode::kBackground, &validateResults, &output)); BSONObj obj = output.obj(); ASSERT_EQ(validateResults.valid, valid); @@ -255,6 +250,27 @@ TEST_F(BackgroundCollectionValidationTest, BackgroundValidateError) { /*runForegroundAsWell*/ true); } +// Verify calling validate() with enforceFastCount=true. +TEST_F(CollectionValidationTest, ValidateEnforceFastCount) { + auto opCtx = operationContext(); + foregroundValidate(opCtx, + /*valid*/ true, + /*numRecords*/ insertDataRange(opCtx, 0, 5), + /*numInvalidDocuments*/ 0, + /*numErrors*/ 0, + {CollectionValidation::ValidateMode::kForegroundFullEnforceFastCount}); +} +TEST_F(CollectionValidationTest, ValidateEnforceFastCountError) { + FailPointEnableBlock failPoint("ephemeralForTestReturnIncorrectNumRecords"); + auto opCtx = operationContext(); + foregroundValidate(opCtx, + /*valid*/ false, + /*numRecords*/ insertDataRange(opCtx, 0, 5), + /*numInvalidDocuments*/ 0, + /*numErrors*/ 1, + {CollectionValidation::ValidateMode::kForegroundFullEnforceFastCount}); +} + /** * Waits for a parallel running collection validation operation to start and then hang at a * failpoint. @@ -298,7 +314,11 @@ TEST_F(BackgroundCollectionValidationTest, BackgroundValidateRunsConcurrentlyWit runBackgroundValidate.join(); // Run regular foreground collection validation to make sure everything is OK. - foregroundValidate(opCtx, /*valid*/ true, /*numRecords*/ numRecords + numRecords2, 0, 0); + foregroundValidate(opCtx, + /*valid*/ true, + /*numRecords*/ numRecords + numRecords2, + 0, + 0); } } // namespace diff --git a/src/mongo/db/catalog/validate_adaptor.cpp b/src/mongo/db/catalog/validate_adaptor.cpp index 2305d26e88e..2b7b877b157 100644 --- a/src/mongo/db/catalog/validate_adaptor.cpp +++ b/src/mongo/db/catalog/validate_adaptor.cpp @@ -380,6 +380,16 @@ void ValidateAdaptor::traverseRecordStore(OperationContext* opCtx, } } + const auto fastCount = _validateState->getCollection()->numRecords(opCtx); + if (_validateState->shouldEnforceFastCount() && + fastCount != static_cast<uint64_t>(_numRecords)) { + results->errors.push_back(str::stream() << "fast count (" << fastCount + << ") does not match number of records (" + << _numRecords << ") for collection '" + << _validateState->getCollection()->ns() << "'"); + results->valid = false; + } + // Do not update the record store stats if we're in the background as we've validated a // checkpoint and it may not have the most up-to-date changes. if (results->valid && !_validateState->isBackground()) { diff --git a/src/mongo/db/catalog/validate_state.cpp b/src/mongo/db/catalog/validate_state.cpp index e3aaf085d76..a85352f9c49 100644 --- a/src/mongo/db/catalog/validate_state.cpp +++ b/src/mongo/db/catalog/validate_state.cpp @@ -53,17 +53,15 @@ namespace CollectionValidation { ValidateState::ValidateState(OperationContext* opCtx, const NamespaceString& nss, - bool background, - ValidateOptions options, + ValidateMode mode, bool turnOnExtraLoggingForTest) : _nss(nss), - _background(background), - _options(options), + _mode(mode), _dataThrottle(opCtx), _extraLoggingForTest(turnOnExtraLoggingForTest) { // Subsequent re-locks will use the UUID when 'background' is true. - if (_background) { + if (isBackground()) { // We need to hold the global lock throughout the entire validation to avoid having to save // and restore our cursors used throughout. This is done in order to avoid abandoning the // snapshot and invalidating our cursors. @@ -93,7 +91,7 @@ ValidateState::ValidateState(OperationContext* opCtx, } void ValidateState::yield(OperationContext* opCtx) { - if (_background) { + if (isBackground()) { _yieldLocks(opCtx); } else { _yieldCursors(opCtx); @@ -101,7 +99,7 @@ void ValidateState::yield(OperationContext* opCtx) { } void ValidateState::_yieldLocks(OperationContext* opCtx) { - invariant(_background); + invariant(isBackground()); // Drop and reacquire the locks. _relockDatabaseAndCollection(opCtx); @@ -123,7 +121,7 @@ void ValidateState::_yieldLocks(OperationContext* opCtx) { }; void ValidateState::_yieldCursors(OperationContext* opCtx) { - invariant(!_background); + invariant(!isBackground()); // Save all the cursors. for (const auto& indexCursor : _indexCursors) { @@ -150,7 +148,7 @@ void ValidateState::initializeCursors(OperationContext* opCtx) { // Background validation will read from a snapshot opened on the all durable timestamp instead // of the latest data. This allows concurrent writes to go ahead without interfering with // validation's view of the data. - if (_background) { + if (isBackground()) { invariant(!opCtx->lockState()->isCollectionLockedForMode(_nss, MODE_X)); opCtx->recoveryUnit()->abandonSnapshot(); // Background validation is expecting to read from the all durable timestamp, but @@ -168,7 +166,7 @@ void ValidateState::initializeCursors(OperationContext* opCtx) { // We want to share the same data throttle instance across all the cursors used during this // validation. Validations started on other collections will not share the same data // throttle instance. - if (!_background) { + if (!isBackground()) { _dataThrottle.turnThrottlingOff(); } @@ -207,7 +205,7 @@ void ValidateState::initializeCursors(OperationContext* opCtx) { } void ValidateState::_relockDatabaseAndCollection(OperationContext* opCtx) { - invariant(_background); + invariant(isBackground()); _collectionLock.reset(); _databaseLock.reset(); diff --git a/src/mongo/db/catalog/validate_state.h b/src/mongo/db/catalog/validate_state.h index da58dc0b51f..bd9287fb463 100644 --- a/src/mongo/db/catalog/validate_state.h +++ b/src/mongo/db/catalog/validate_state.h @@ -61,8 +61,7 @@ public: */ ValidateState(OperationContext* opCtx, const NamespaceString& nss, - bool background, - ValidateOptions options, + ValidateMode mode, bool turnOnExtraLoggingForTest = false); const NamespaceString& nss() const { @@ -70,15 +69,20 @@ public: } bool isBackground() const { - return _background; + return _mode == ValidateMode::kBackground; } - bool isFullCollectionValidation() const { - return (_options & ValidateOptions::kFullRecordStoreValidation); + bool shouldEnforceFastCount() const { + return _mode == ValidateMode::kForegroundFullEnforceFastCount; + } + + bool isFullValidation() const { + return _mode == ValidateMode::kForegroundFull || + _mode == ValidateMode::kForegroundFullEnforceFastCount; } bool isFullIndexValidation() const { - return (_options & ValidateOptions::kFullIndexValidation); + return isFullValidation() || _mode == ValidateMode::kForegroundFullIndexOnly; } const UUID uuid() const { @@ -155,7 +159,7 @@ private: /** * Re-locks the database and collection with the appropriate locks for background validation. - * This should only be called when '_background' is set to true. + * This should only be called when '_mode' is set to 'kBackground'. */ void _relockDatabaseAndCollection(OperationContext* opCtx); @@ -186,8 +190,7 @@ private: void _yieldCursors(OperationContext* opCtx); NamespaceString _nss; - bool _background; - ValidateOptions _options; + ValidateMode _mode; OptionalCollectionUUID _uuid; boost::optional<Lock::GlobalLock> _globalLock; diff --git a/src/mongo/db/catalog/validate_state_test.cpp b/src/mongo/db/catalog/validate_state_test.cpp index 9848788942f..5b4a99a4b86 100644 --- a/src/mongo/db/catalog/validate_state_test.cpp +++ b/src/mongo/db/catalog/validate_state_test.cpp @@ -117,18 +117,12 @@ TEST_F(ValidateStateTest, NonExistentCollectionShouldThrowNamespaceNotFoundError auto opCtx = operationContext(); ASSERT_THROWS_CODE(CollectionValidation::ValidateState( - opCtx, - kNss, - /*background*/ false, - CollectionValidation::ValidateOptions::kNoFullValidation), + opCtx, kNss, CollectionValidation::ValidateMode::kForeground), AssertionException, ErrorCodes::NamespaceNotFound); ASSERT_THROWS_CODE(CollectionValidation::ValidateState( - opCtx, - kNss, - /*background*/ true, - CollectionValidation::ValidateOptions::kNoFullValidation), + opCtx, kNss, CollectionValidation::ValidateMode::kBackground), AssertionException, ErrorCodes::NamespaceNotFound); } @@ -144,7 +138,7 @@ TEST_F(ValidateStateTest, UncheckpointedCollectionShouldBeAbleToInitializeCursor createCollectionAndPopulateIt(opCtx, kNss); CollectionValidation::ValidateState validateState( - opCtx, kNss, /*background*/ true, CollectionValidation::ValidateOptions::kNoFullValidation); + opCtx, kNss, CollectionValidation::ValidateMode::kBackground); // Assert that cursors are able to created on the new collection. validateState.initializeCursors(opCtx); // There should only be a first record id if cursors were initialized successfully. @@ -178,10 +172,7 @@ TEST_F(ValidateStateTest, OpenCursorsOnAllIndexes) { { // Open the cursors. CollectionValidation::ValidateState validateState( - opCtx, - kNss, - /*background*/ false, - CollectionValidation::ValidateOptions::kNoFullValidation); + opCtx, kNss, CollectionValidation::ValidateMode::kForeground); validateState.initializeCursors(opCtx); // Make sure all of the indexes were found and cursors opened against them. Including the @@ -196,10 +187,7 @@ TEST_F(ValidateStateTest, OpenCursorsOnAllIndexes) { // Check that foreground validation behaves just the same with checkpoint'ed data. CollectionValidation::ValidateState validateState( - opCtx, - kNss, - /*background*/ false, - CollectionValidation::ValidateOptions::kNoFullValidation); + opCtx, kNss, CollectionValidation::ValidateMode::kForeground); validateState.initializeCursors(opCtx); ASSERT_EQ(validateState.getIndexes().size(), 5); } @@ -234,7 +222,7 @@ TEST_F(ValidateStateTest, OpenCursorsOnAllIndexesWithBackground) { // Open the cursors. CollectionValidation::ValidateState validateState( - opCtx, kNss, /*background*/ true, CollectionValidation::ValidateOptions::kNoFullValidation); + opCtx, kNss, CollectionValidation::ValidateMode::kBackground); validateState.initializeCursors(opCtx); // We should be able to open a cursor on each index. @@ -279,10 +267,7 @@ TEST_F(ValidateStateTest, CursorsAreNotOpenedAgainstCheckpointedIndexesThatWereL // (Note the _id index was create with collection creation, so we have 3 indexes.) { CollectionValidation::ValidateState validateState( - opCtx, - kNss, - /*background*/ true, - CollectionValidation::ValidateOptions::kNoFullValidation); + opCtx, kNss, CollectionValidation::ValidateMode::kBackground); validateState.initializeCursors(opCtx); ASSERT_EQ(validateState.getIndexes().size(), 3); } @@ -292,7 +277,7 @@ TEST_F(ValidateStateTest, CursorsAreNotOpenedAgainstCheckpointedIndexesThatWereL opCtx->recoveryUnit()->waitUntilUnjournaledWritesDurable(opCtx, /*stableCheckpoint*/ false); CollectionValidation::ValidateState validateState( - opCtx, kNss, /*background*/ true, CollectionValidation::ValidateOptions::kNoFullValidation); + opCtx, kNss, CollectionValidation::ValidateMode::kBackground); validateState.initializeCursors(opCtx); ASSERT_EQ(validateState.getIndexes().size(), 3); } diff --git a/src/mongo/db/commands/validate.cpp b/src/mongo/db/commands/validate.cpp index badb47a4852..50c22f6b87c 100644 --- a/src/mongo/db/commands/validate.cpp +++ b/src/mongo/db/commands/validate.cpp @@ -140,12 +140,20 @@ public: << " and { full: true } is not supported."); } + const bool enforceFastCount = cmdObj["enforceFastCount"].trueValue(); + if (background && enforceFastCount) { + uasserted(ErrorCodes::CommandNotSupported, + str::stream() << "Running the validate command with both { background: true }" + << " and { enforceFastCount: true } is not supported."); + } + if (!serverGlobalParams.quiet.load()) { LOGV2(20514, "CMD: validate", "namespace"_attr = nss, "background"_attr = background, - "full"_attr = fullValidate); + "full"_attr = fullValidate, + "enforceFastCount"_attr = enforceFastCount); } // Only one validation per collection can be in progress, the rest wait. @@ -172,12 +180,17 @@ public: _validationNotifier.notify_all(); }); - auto options = (fullValidate) ? CollectionValidation::ValidateOptions::kFullValidation - : CollectionValidation::ValidateOptions::kNoFullValidation; - + auto mode = [&] { + if (background) + return CollectionValidation::ValidateMode::kBackground; + if (enforceFastCount) + return CollectionValidation::ValidateMode::kForegroundFullEnforceFastCount; + if (fullValidate) + return CollectionValidation::ValidateMode::kForegroundFull; + return CollectionValidation::ValidateMode::kForeground; + }(); ValidateResults validateResults; - Status status = CollectionValidation::validate( - opCtx, nss, options, background, &validateResults, &result); + Status status = CollectionValidation::validate(opCtx, nss, mode, &validateResults, &result); if (!status.isOK()) { return CommandHelpers::appendCommandStatusNoThrow(result, status); } diff --git a/src/mongo/db/repair_database.cpp b/src/mongo/db/repair_database.cpp index 8a926d3eaaf..c662a63fef6 100644 --- a/src/mongo/db/repair_database.cpp +++ b/src/mongo/db/repair_database.cpp @@ -163,13 +163,14 @@ Status repairCollections(OperationContext* opCtx, ValidateResults validateResults; BSONObjBuilder output; - // Set options to exclude FullRecordStoreValidation because we have already validated the - // underlying record store in the call to repairRecordStore above. - auto options = CollectionValidation::ValidateOptions::kFullIndexValidation; - - const bool background = false; + // Exclude full record store validation because we have already validated the underlying + // record store in the call to repairRecordStore above. status = CollectionValidation::validate( - opCtx, nss, options, background, &validateResults, &output); + opCtx, + nss, + CollectionValidation::ValidateMode::kForegroundFullIndexOnly, + &validateResults, + &output); if (!status.isOK()) { return status; } diff --git a/src/mongo/db/repl/idempotency_test_fixture.cpp b/src/mongo/db/repl/idempotency_test_fixture.cpp index efaf0b106ae..fc54c2f3933 100644 --- a/src/mongo/db/repl/idempotency_test_fixture.cpp +++ b/src/mongo/db/repl/idempotency_test_fixture.cpp @@ -405,8 +405,7 @@ CollectionState IdempotencyTest::validate(const NamespaceString& nss) { ASSERT_OK( CollectionValidation::validate(_opCtx.get(), nss, - CollectionValidation::ValidateOptions::kFullValidation, - false, + CollectionValidation::ValidateMode::kForegroundFull, &validateResults, &bob)); ASSERT_TRUE(validateResults.valid); diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp index ceef547d63a..e7145216cf2 100644 --- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp +++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp @@ -39,11 +39,14 @@ #include "mongo/db/storage/oplog_hack.h" #include "mongo/db/storage/recovery_unit.h" #include "mongo/logv2/log.h" +#include "mongo/util/fail_point.h" #include "mongo/util/str.h" #include "mongo/util/unowned_ptr.h" namespace mongo { +MONGO_FAIL_POINT_DEFINE(ephemeralForTestReturnIncorrectNumRecords); + using std::shared_ptr; class EphemeralForTestRecordStore::InsertChange : public RecoveryUnit::Change { @@ -560,6 +563,13 @@ int64_t EphemeralForTestRecordStore::storageSize(OperationContext* opCtx, return _data->dataSize + recordOverhead; } +long long EphemeralForTestRecordStore::numRecords(OperationContext* opCtx) const { + if (MONGO_unlikely(ephemeralForTestReturnIncorrectNumRecords.shouldFail())) { + return _data->records.size() + 1; + } + return _data->records.size(); +} + RecordId EphemeralForTestRecordStore::allocateLoc(WithLock) { RecordId out = RecordId(_data->nextId++); invariant(out.isNormal()); diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h index a29bc0fa014..c37b69ba3e4 100644 --- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h +++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h @@ -103,9 +103,7 @@ public: return _data->dataSize; } - virtual long long numRecords(OperationContext* opCtx) const { - return _data->records.size(); - } + virtual long long numRecords(OperationContext* opCtx) const; virtual boost::optional<RecordId> oplogStartHack(OperationContext* opCtx, const RecordId& startingPosition) const; diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp index c45d42b2d8d..17538a57289 100644 --- a/src/mongo/dbtests/validate_tests.cpp +++ b/src/mongo/dbtests/validate_tests.cpp @@ -103,14 +103,17 @@ protected: _opCtx.recoveryUnit()->waitUntilUnjournaledWritesDurable(&_opCtx, /*stableCheckpoint*/ false); - auto options = (_full) ? CollectionValidation::ValidateOptions::kFullValidation - : CollectionValidation::ValidateOptions::kNoFullValidation; - + auto mode = [&] { + if (_background) + return CollectionValidation::ValidateMode::kBackground; + return _full ? CollectionValidation::ValidateMode::kForegroundFull + : CollectionValidation::ValidateMode::kForeground; + }(); ValidateResults results; BSONObjBuilder output; ASSERT_OK(CollectionValidation::validate( - &_opCtx, _nss, options, _background, &results, &output, kTurnOnExtraLoggingForTest)); + &_opCtx, _nss, mode, &results, &output, kTurnOnExtraLoggingForTest)); // Check if errors are reported if and only if valid is set to false. ASSERT_EQ(results.valid, results.errors.empty()); @@ -1210,14 +1213,13 @@ public: ValidateResults results; BSONObjBuilder output; - ASSERT_OK(CollectionValidation::validate( - &_opCtx, - _nss, - CollectionValidation::ValidateOptions::kFullValidation, - _background, - &results, - &output, - kTurnOnExtraLoggingForTest)); + ASSERT_OK( + CollectionValidation::validate(&_opCtx, + _nss, + CollectionValidation::ValidateMode::kForegroundFull, + &results, + &output, + kTurnOnExtraLoggingForTest)); auto dumpOnErrorGuard = makeGuard([&] { StorageDebugUtil::printValidateResults(results); @@ -1336,14 +1338,13 @@ public: ValidateResults results; BSONObjBuilder output; - ASSERT_OK(CollectionValidation::validate( - &_opCtx, - _nss, - CollectionValidation::ValidateOptions::kFullValidation, - _background, - &results, - &output, - kTurnOnExtraLoggingForTest)); + ASSERT_OK( + CollectionValidation::validate(&_opCtx, + _nss, + CollectionValidation::ValidateMode::kForegroundFull, + &results, + &output, + kTurnOnExtraLoggingForTest)); auto dumpOnErrorGuard = makeGuard([&] { StorageDebugUtil::printValidateResults(results); @@ -1435,14 +1436,13 @@ public: ValidateResults results; BSONObjBuilder output; - ASSERT_OK(CollectionValidation::validate( - &_opCtx, - _nss, - CollectionValidation::ValidateOptions::kFullValidation, - _background, - &results, - &output, - kTurnOnExtraLoggingForTest)); + ASSERT_OK( + CollectionValidation::validate(&_opCtx, + _nss, + CollectionValidation::ValidateMode::kForegroundFull, + &results, + &output, + kTurnOnExtraLoggingForTest)); auto dumpOnErrorGuard = makeGuard([&] { StorageDebugUtil::printValidateResults(results); diff --git a/src/mongo/shell/servers.js b/src/mongo/shell/servers.js index 94638d450c7..ddf1ab9bc6a 100644 --- a/src/mongo/shell/servers.js +++ b/src/mongo/shell/servers.js @@ -995,6 +995,10 @@ MongoRunner.stopMongod = function(conn, signal, opts, waitpid) { opts = opts || {}; waitpid = (waitpid === undefined) ? true : waitpid; + if (signal !== MongoRunner.EXIT_CLEAN) { + TestData.allowUncleanShutdowns = true; + } + var allowedExitCode = MongoRunner.EXIT_CLEAN; if (opts.allowedExitCode) { |