SERVER-117510 Add replicated fastcount validate check skeleton (#46981)

GitOrigin-RevId: b5154dba54f04bc9eecd1c601c8793c8e1a615e6
This commit is contained in:
Matt Kneiser 2026-01-27 10:23:48 -08:00 committed by MongoDB Bot
parent 4969dec857
commit 5fb893bc95
3 changed files with 133 additions and 8 deletions

View File

@ -1298,12 +1298,35 @@ void ValidateAdaptor::traverseRecordStore(OperationContext* opCtx,
<< " invalid documents.");
}
const auto fastCount = coll->numRecords(opCtx);
if (_validateState->shouldEnforceFastCount() && fastCount != _numRecords) {
results->addError(str::stream()
<< "fast count (" << fastCount << ") does not match number of records ("
<< _numRecords << ") for collection '" << coll->ns().toStringForErrorMsg()
<< "'");
if (_validateState->shouldEnforceFastCount()) {
auto fastCountType = _validateState->getDetectedFastCountType(opCtx);
switch (fastCountType) {
case CollectionValidation::FastCountType::legacySizeStorer:
if (const auto fastCount = coll->numRecords(opCtx); fastCount != _numRecords) {
results->addError(str::stream() << "fast count (" << fastCount
<< ") does not match number of records ("
<< _numRecords << ") for collection '"
<< coll->ns().toStringForErrorMsg() << "'");
}
break;
case CollectionValidation::FastCountType::replicated:
if (const auto fastCount = coll->numRecords(opCtx); fastCount != _numRecords) {
results->addError(str::stream() << "replicated fast count (" << fastCount
<< ") does not match number of records ("
<< _numRecords << ") for collection '"
<< coll->ns().toStringForErrorMsg() << "'");
}
break;
case CollectionValidation::FastCountType::both:
uasserted(ErrorCodes::InvalidOptions, "Both FastCount tables found");
break;
case CollectionValidation::FastCountType::none:
uasserted(ErrorCodes::InvalidOptions, "No FastCount tables found");
break;
case CollectionValidation::FastCountType::invalid:
uasserted(ErrorCodes::InvalidOptions, "No FastCount tables found");
break;
}
}
// Do not update the record store stats if we're in the background as we've validated a

View File

@ -114,6 +114,72 @@ ValidateState::ValidateState(OperationContext* opCtx,
if (adjustMultikey()) {
invariant(!isBackground());
}
if (false) { // TODO(SERVER-117795): Check feature flag here
if (enforceFastCountRequested()) {
auto fastCountType = getDetectedFastCountType(opCtx);
uassert(ErrorCodes::InvalidOptions,
"Both FastCount tables found",
fastCountType != FastCountType::both);
}
}
}
// TODO(SERVER-117795)
Status ValidateState::_getReplicatedFastCountCollection(OperationContext* opCtx) const {
// try {
// auto fastCountNss = NamespaceString::makeGlobalConfigCollection(
// NamespaceString::kSystemReplicatedSizeAndCountMetadataStore);
// boost::optional<CollectionOrViewAcquisition> acquisition =
// acquireCollectionOrViewMaybeLockFree(
// opCtx,
// CollectionOrViewAcquisitionRequest::fromOpCtx(
// opCtx, fastCountNss, AcquisitionPrerequisites::OperationType::kRead));
// if (!acquisition || !acquisition->collectionExists()) {
// return Status(
// ErrorCodes::NamespaceNotFound,
// str::stream()
// << "Internal FastCount Collection '" << fastCountNss.toStringForErrorMsg()
// << "' does not exist to validate. Required for enforcing fast count.");
// }
// // TODO(SERVER-117795): Is this needed? Unused at the moment, might be needed?
// // _fastCountCollection = std::move(acquisition);
// } catch (const ExceptionFor<ErrorCodes::SnapshotTooOld>&) {
// // TODO(SERVER-117795): Is this the right exception? Is it relevant?
// if (isBackground()) {
// // This will throw SnapshotTooOld to indicate we cannot find an available snapshot at
// // the provided timestamp. This is likely because minSnapshotHistoryWindowInSeconds
// has
// // been changed to a lower value from the default of 5 minutes.
// return Status(
// ErrorCodes::NamespaceNotFound,
// fmt::format("Cannot run background validation on collection {} because the "
// "snapshot history is no longer available",
// _nss.toStringForErrorMsg()));
// }
// throw;
// }
return Status::OK();
}
// TODO(SERVER-117795): Get state from the storage engine.
Status ValidateState::_getUnreplicatedFastCountCollection(OperationContext* opCtx) const {
try {
// std::string filename = ident::kSizeStorer + ".wt";
// boost::filesystem::path sizeStorerAbsoluteFilePath =
// boost::filesystem::path(storageGlobalParams.dbpath) / filename;
// if (boost::filesystem::exists(sizeStorerAbsoluteFilePath)) {
// return Status::OK();
// }
return Status::OK();
// return Status(ErrorCodes::NonExistentPath, "SizeStorer doesn't exist");
} catch (...) {
return exceptionToStatus();
}
}
bool ValidateState::shouldEnforceFastCount() const {
@ -150,6 +216,22 @@ bool ValidateState::shouldEnforceFastCount() const {
return false;
}
FastCountType ValidateState::getDetectedFastCountType(OperationContext* opCtx) const {
// TODO(SERVER-117795): Uncomment.
return FastCountType::legacySizeStorer;
// auto replicatedFastCountStatus = _getReplicatedFastCountCollection(opCtx);
// auto legacyFastCountStatus = _getUnreplicatedFastCountCollection(opCtx);
// if (replicatedFastCountStatus.isOK() && legacyFastCountStatus.isOK()) {
// return FastCountType::both;
// } else if (replicatedFastCountStatus.isOK()) {
// return FastCountType::replicated;
// } else if (legacyFastCountStatus.isOK()) {
// return FastCountType::legacySizeStorer;
// } else {
// return FastCountType::none;
// }
}
void ValidateState::yieldCursors(OperationContext* opCtx) {
// Save all the cursors.
for (const auto& indexCursor : _indexCursors) {

View File

@ -73,10 +73,18 @@ namespace CollectionValidation {
*/
MONGO_MOD_PUBLIC Lock::ExclusiveLock obtainExclusiveValidationLock(OperationContext* opCtx);
enum struct FastCountType {
legacySizeStorer,
replicated,
both,
none,
invalid,
};
/**
* Contains information about the collection being validated and the user provided validation
* options. Additionally it maintains the state of shared objects throughtout the validation, such
* as locking, cursors and data throttling.
* options. Additionally it maintains the state of shared objects throughout the validation, such
* as locking, cursors, and data throttling.
*/
class ValidateState : public ValidationOptions {
ValidateState(const ValidateState&) = delete;
@ -89,8 +97,14 @@ public:
return _nss;
}
/**
* Returns true if fast count is being validated, and the collection
* supports fast count. Certain internal collections are not supported by fast count.
*/
bool shouldEnforceFastCount() const;
FastCountType getDetectedFastCountType(OperationContext* opCtx) const;
BSONValidateModeEnum getBSONValidateMode() const {
return isBSONConformanceValidation() ? BSONValidateModeEnum::kFull
: BSONValidateModeEnum::kExtended;
@ -151,6 +165,12 @@ public:
private:
ValidateState() = delete;
/**
* These functions use catalog and on-disk state to determine which system is being used.
*/
Status _getReplicatedFastCountCollection(OperationContext* opCtx) const;
Status _getUnreplicatedFastCountCollection(OperationContext* opCtx) const;
// This lock needs to be obtained before the global lock. Initialise in the validation
// constructor. Oplog Batch Applier takes this lock in exclusive mode when applying the batch.
// Foreground validation waits on this lock to begin.