summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/concurrency/fsm_libs/cluster.js9
-rw-r--r--jstests/libs/override_methods/validate_collections_on_shutdown.js9
-rw-r--r--jstests/replsets/prepare_conflict_read_concern_behavior.js4
-rw-r--r--jstests/replsets/retrying_prepared_transaction_does_not_block_stepdown.js5
-rw-r--r--jstests/replsets/rollback_transaction_table.js3
-rw-r--r--src/mongo/db/catalog/collection_validation.cpp13
-rw-r--r--src/mongo/db/catalog/collection_validation.h36
-rw-r--r--src/mongo/db/catalog/collection_validation_test.cpp56
-rw-r--r--src/mongo/db/catalog/validate_adaptor.cpp10
-rw-r--r--src/mongo/db/catalog/validate_state.cpp20
-rw-r--r--src/mongo/db/catalog/validate_state.h21
-rw-r--r--src/mongo/db/catalog/validate_state_test.cpp31
-rw-r--r--src/mongo/db/commands/validate.cpp25
-rw-r--r--src/mongo/db/repair_database.cpp13
-rw-r--r--src/mongo/db/repl/idempotency_test_fixture.cpp3
-rw-r--r--src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp10
-rw-r--r--src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h4
-rw-r--r--src/mongo/dbtests/validate_tests.cpp56
-rw-r--r--src/mongo/shell/servers.js4
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) {