diff options
Diffstat (limited to 'src/mongo/db/catalog/collection_validation.cpp')
| -rw-r--r-- | src/mongo/db/catalog/collection_validation.cpp | 216 |
1 files changed, 86 insertions, 130 deletions
diff --git a/src/mongo/db/catalog/collection_validation.cpp b/src/mongo/db/catalog/collection_validation.cpp index 9d0c487b41a..42c5ac18dc7 100644 --- a/src/mongo/db/catalog/collection_validation.cpp +++ b/src/mongo/db/catalog/collection_validation.cpp @@ -78,7 +78,8 @@ void _validateIndexesInternalStructure(OperationContext* opCtx, // Need to use the IndexCatalog here because the 'validateState->indexes' object hasn't been // constructed yet. It must be initialized to ensure we're validating all indexes. const IndexCatalog* indexCatalog = validateState->getCollection()->getIndexCatalog(); - const auto it = indexCatalog->getIndexIterator(opCtx, IndexCatalog::InclusionPolicy::kReady); + const std::unique_ptr<IndexCatalog::IndexIterator> it = + indexCatalog->getIndexIterator(opCtx, false); // Validate Indexes Internal Structure, checking if index files have been compromised or // corrupted. @@ -97,11 +98,14 @@ void _validateIndexesInternalStructure(OperationContext* opCtx, auto& curIndexResults = (results->indexResultsMap)[descriptor->indexName()]; - iam->validate(opCtx, nullptr, &curIndexResults); + int64_t numValidated; + iam->validate(opCtx, &numValidated, &curIndexResults); if (!curIndexResults.valid) { results->valid = false; } + + curIndexResults.keysTraversedFromFullValidate = numValidated; } } @@ -133,6 +137,33 @@ void _validateIndexes(OperationContext* opCtx, auto& curIndexResults = (results->indexResultsMap)[descriptor->indexName()]; curIndexResults.keysTraversed = numTraversedKeys; + // If we are performing a full index validation, we have information on the number of index + // keys validated in _validateIndexesInternalStructure (when we validated the internal + // structure of the index). Check if this is consistent with 'numTraversedKeys' from + // traverseIndex above. + if (validateState->isFullIndexValidation()) { + invariant(opCtx->lockState()->isCollectionLockedForMode(validateState->nss(), MODE_X)); + + // The number of keys counted in _validateIndexesInternalStructure, when checking the + // internal structure of the index. + const int64_t numIndexKeys = curIndexResults.keysTraversedFromFullValidate; + + // Check if currIndexResults is valid to ensure that this index is not corrupted or + // comprised (which was set in _validateIndexesInternalStructure). If the index is + // corrupted, there is no use in checking if the traversal yielded the same key count. + if (curIndexResults.valid) { + if (numIndexKeys != numTraversedKeys) { + curIndexResults.valid = false; + string msg = str::stream() + << "number of traversed index entries (" << numTraversedKeys + << ") does not match the number of expected index entries (" << numIndexKeys + << ")"; + results->errors.push_back(msg); + results->valid = false; + } + } + } + if (!curIndexResults.valid) { results->valid = false; } @@ -162,8 +193,7 @@ void _gatherIndexEntryErrors(OperationContext* opCtx, ValidateResults tempValidateResults; BSONObjBuilder tempBuilder; - indexValidator->traverseRecordStore( - opCtx, &tempValidateResults, &tempBuilder, validateState->validationVersion()); + indexValidator->traverseRecordStore(opCtx, &tempValidateResults, &tempBuilder); } LOGV2_OPTIONS( @@ -200,7 +230,7 @@ void _gatherIndexEntryErrors(OperationContext* opCtx, LOGV2_OPTIONS(20301, {LogComponent::kIndex}, "Finished traversing through all the indexes"); - indexConsistency->addIndexEntryErrors(opCtx, result); + indexConsistency->addIndexEntryErrors(result); } void _validateIndexKeyCount(OperationContext* opCtx, @@ -217,94 +247,6 @@ void _validateIndexKeyCount(OperationContext* opCtx, } } -void _printIndexSpec(const ValidateState* validateState, StringData indexName) { - auto& indexes = validateState->getIndexes(); - auto indexEntry = - std::find_if(indexes.begin(), - indexes.end(), - [&](const std::shared_ptr<const IndexCatalogEntry> indexEntry) -> bool { - return indexEntry->descriptor()->indexName() == indexName; - }); - if (indexEntry != indexes.end()) { - auto indexSpec = (*indexEntry)->descriptor()->infoObj(); - LOGV2_ERROR(7463100, "Index failed validation", "spec"_attr = indexSpec); - } -} - -/** - * Logs oplog entries related to corrupted records/indexes in validation results. - */ -void _logOplogEntriesForInvalidResults(OperationContext* opCtx, ValidateResults* results) { - if (results->recordTimestamps.empty()) { - return; - } - - LOGV2( - 7464200, - "Validation failed: oplog timestamps referenced by corrupted collection and index entries", - "numTimestamps"_attr = results->recordTimestamps.size()); - - // Set up read on oplog collection. - try { - AutoGetOplog oplogRead(opCtx, OplogAccessMode::kRead); - const auto& oplogCollection = oplogRead.getCollection(); - - if (!oplogCollection) { - for (auto it = results->recordTimestamps.rbegin(); - it != results->recordTimestamps.rend(); - it++) { - const auto& timestamp = *it; - LOGV2(8080900, - " Validation failed: Oplog entry timestamp for corrupted collection and " - "index entry", - "timestamp"_attr = timestamp); - } - return; - } - - // Log oplog entries in reverse from most recent timestamp to oldest. - // Due to oplog truncation, if we fail to find any oplog entry for a particular timestamp, - // we can stop searching for oplog entries with earlier timestamps. - auto recordStore = oplogCollection->getRecordStore(); - uassert(ErrorCodes::InternalError, - "Validation failed: Unable to get oplog record store for corrupted collection and " - "index entries", - recordStore); - - auto cursor = recordStore->getCursor(opCtx, /*forward=*/false); - uassert(ErrorCodes::CursorNotFound, - "Validation failed: Unable to get cursor to oplog collection.", - cursor); - - for (auto it = results->recordTimestamps.rbegin(); it != results->recordTimestamps.rend(); - it++) { - const auto& timestamp = *it; - - // A record id in the oplog collection is equivalent to the document's timestamp field. - RecordId recordId(timestamp.asULL()); - auto record = cursor->seekExact(recordId); - if (!record) { - LOGV2(7464201, - " Validation failed: Stopping oplog entry search for corrupted collection " - "and index entries.", - "timestamp"_attr = timestamp); - break; - } - - LOGV2( - 7464202, - " Validation failed: Oplog entry found for corrupted collection and index entry", - "timestamp"_attr = timestamp, - "oplogEntryDoc"_attr = redact(record->data.toBson())); - } - } catch (DBException& ex) { - LOGV2_ERROR(7464203, - "Validation failed: Unable to fetch entries from oplog collection for " - "corrupted collection and index entries", - "ex"_attr = ex); - } -} - void _reportValidationResults(OperationContext* opCtx, ValidateState* validateState, ValidateResults* results, @@ -321,19 +263,17 @@ void _reportValidationResults(OperationContext* opCtx, // Report detailed index validation results gathered when using {full: true} for validated // indexes. - int nIndexes = results->indexResultsMap.size(); - for (const auto& [indexName, vr] : results->indexResultsMap) { - if (!vr.valid) { - results->valid = false; - _printIndexSpec(validateState, indexName); + for (const auto& index : validateState->getIndexes()) { + const std::string indexName = index->descriptor()->indexName(); + auto& indexResultsMap = results->indexResultsMap; + if (indexResultsMap.find(indexName) == indexResultsMap.end()) { + continue; } - if (validateState->getSkippedIndexes().contains(indexName)) { - // Index internal state was checked and cleared, so it was reported in indexResultsMap, - // but we did not verify the index contents against the collection, so we should exclude - // it from this report. - --nIndexes; - continue; + auto& vr = indexResultsMap.at(indexName); + + if (!vr.valid) { + results->valid = false; } BSONObjBuilder bob(indexDetails.subobjStart(indexName)); @@ -354,7 +294,7 @@ void _reportValidationResults(OperationContext* opCtx, results->errors.insert(results->errors.end(), vr.errors.begin(), vr.errors.end()); } - output->append("nIndexes", nIndexes); + output->append("nIndexes", static_cast<int>(validateState->getIndexes().size())); output->append("keysPerIndex", keysPerIndex.done()); output->append("indexDetails", indexDetails.done()); } @@ -364,7 +304,6 @@ void _reportInvalidResults(OperationContext* opCtx, ValidateResults* results, BSONObjBuilder* output) { _reportValidationResults(opCtx, validateState, results, output); - _logOplogEntriesForInvalidResults(opCtx, results); LOGV2_OPTIONS(20302, {LogComponent::kIndex}, "Validation complete -- Corruption found", @@ -402,6 +341,35 @@ void addErrorIfUnequal(boost::optional<ValidationActionEnum> stored, results); } +std::string multikeyPathsToString(MultikeyPaths paths) { + str::stream builder; + builder << "["; + auto pathIt = paths.begin(); + while (true) { + builder << "{"; + + auto pathSet = *pathIt; + auto setIt = pathSet.begin(); + while (true) { + builder << *setIt++; + if (setIt == pathSet.end()) { + break; + } else { + builder << ","; + } + } + builder << "}"; + + if (++pathIt == paths.end()) { + break; + } else { + builder << ","; + } + } + builder << "]"; + return builder; +} + void _validateCatalogEntry(OperationContext* opCtx, ValidateState* validateState, ValidateResults* results) { @@ -442,23 +410,20 @@ void _validateCatalogEntry(OperationContext* opCtx, } const auto& indexCatalog = collection->getIndexCatalog(); - auto indexIt = indexCatalog->getIndexIterator(opCtx, - IndexCatalog::InclusionPolicy::kReady | - IndexCatalog::InclusionPolicy::kUnfinished | - IndexCatalog::InclusionPolicy::kFrozen); + auto indexIt = indexCatalog->getIndexIterator(opCtx, /*includeUnfinishedIndexes=*/true); while (indexIt->more()) { const IndexCatalogEntry* indexEntry = indexIt->next(); const std::string indexName = indexEntry->descriptor()->indexName(); Status status = - index_key_validate::validateIndexSpec(opCtx, indexEntry->descriptor()->infoObj()) - .getStatus(); + index_key_validate::validateIndexSpecFieldNames(indexEntry->descriptor()->infoObj()); if (!status.isOK()) { - results->warnings.push_back( - fmt::format("The index specification for index '{}' contains invalid fields. {}. " - "Run the 'collMod' command on the collection without any arguments " - "to fix the invalid index options", + results->valid = false; + results->errors.push_back( + fmt::format("The index specification for index '{}' contains invalid field names. " + "{}. Run the 'collMod' command on the collection without any arguments " + "to remove the invalid index options", indexName, status.reason())); } @@ -594,15 +559,14 @@ Status validate(OperationContext* opCtx, const NamespaceString& nss, ValidateMode mode, RepairMode repairMode, - const AdditionalOptions& additionalOptions, ValidateResults* results, BSONObjBuilder* output, - bool logDiagnostics) { + bool turnOnExtraLoggingForTest) { invariant(!opCtx->lockState()->isLocked() || storageGlobalParams.repair); // 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, mode, repairMode, additionalOptions, logDiagnostics); + ValidateState validateState(opCtx, nss, mode, repairMode, 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 @@ -621,14 +585,6 @@ Status validate(OperationContext* opCtx, opCtx->recoveryUnit()->abandonSnapshot(); opCtx->recoveryUnit()->setPrepareConflictBehavior(oldPrepareConflictBehavior); }); - - // Relax corruption detection so that we log and continue scanning instead of failing early. - auto oldDataCorruptionMode = opCtx->recoveryUnit()->getDataCorruptionDetectionMode(); - opCtx->recoveryUnit()->setDataCorruptionDetectionMode( - DataCorruptionDetectionMode::kLogAndContinue); - ON_BLOCK_EXIT( - [&] { opCtx->recoveryUnit()->setDataCorruptionDetectionMode(oldDataCorruptionMode); }); - if (validateState.fixErrors()) { // Note: cannot set PrepareConflictBehavior here, since the validate command with repair // needs kIngnoreConflictsAllowWrites, but validate repair at startup cannot set that here @@ -705,8 +661,7 @@ Status validate(OperationContext* opCtx, // the collection. For clustered collections, the validator also verifies that the // record key (RecordId) matches the cluster key field in the record value (document's // cluster key). - indexValidator.traverseRecordStore( - opCtx, results, output, additionalOptions.validationVersion); + indexValidator.traverseRecordStore(opCtx, results, output); // Pause collection validation while a lock is held and between collection and index data // validation. @@ -780,7 +735,8 @@ Status validate(OperationContext* opCtx, return e.toStatus(); } string err = str::stream() << "exception during collection validation: " << e.toString(); - results->warnings.push_back(err); + results->errors.push_back(err); + results->valid = false; LOGV2_OPTIONS(5160302, {LogComponent::kIndex}, "Validation failed due to exception", |
