diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-14 14:26:38 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-14 14:26:38 -0300 |
| commit | 294bc6ecabf14c09c9bc8644704921dcf97cb44e (patch) | |
| tree | 279b1e0bab53901a1647ac63c1c724f0f789a663 /src/mongo/db/catalog/collection_validation.cpp | |
| parent | 70be7c27a251621187a1de533462ae2bb1e3bd39 (diff) | |
| parent | 1e917fd798aa25b7066d4b414b51184f13d5a092 (diff) | |
Update upstream source from tag 'upstream/6.0.10'debian/6.0.10-1
Update to upstream version '6.0.10'
with Debian dir 2d176fa254eee97b139f712fec5709641335a8c3
Diffstat (limited to 'src/mongo/db/catalog/collection_validation.cpp')
| -rw-r--r-- | src/mongo/db/catalog/collection_validation.cpp | 144 |
1 files changed, 97 insertions, 47 deletions
diff --git a/src/mongo/db/catalog/collection_validation.cpp b/src/mongo/db/catalog/collection_validation.cpp index 42c5ac18dc7..f6e1e4139e7 100644 --- a/src/mongo/db/catalog/collection_validation.cpp +++ b/src/mongo/db/catalog/collection_validation.cpp @@ -78,8 +78,7 @@ 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 std::unique_ptr<IndexCatalog::IndexIterator> it = - indexCatalog->getIndexIterator(opCtx, false); + const auto it = indexCatalog->getIndexIterator(opCtx, IndexCatalog::InclusionPolicy::kReady); // Validate Indexes Internal Structure, checking if index files have been compromised or // corrupted. @@ -98,14 +97,11 @@ void _validateIndexesInternalStructure(OperationContext* opCtx, auto& curIndexResults = (results->indexResultsMap)[descriptor->indexName()]; - int64_t numValidated; - iam->validate(opCtx, &numValidated, &curIndexResults); + iam->validate(opCtx, nullptr, &curIndexResults); if (!curIndexResults.valid) { results->valid = false; } - - curIndexResults.keysTraversedFromFullValidate = numValidated; } } @@ -137,33 +133,6 @@ 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; } @@ -230,7 +199,7 @@ void _gatherIndexEntryErrors(OperationContext* opCtx, LOGV2_OPTIONS(20301, {LogComponent::kIndex}, "Finished traversing through all the indexes"); - indexConsistency->addIndexEntryErrors(result); + indexConsistency->addIndexEntryErrors(opCtx, result); } void _validateIndexKeyCount(OperationContext* opCtx, @@ -247,6 +216,81 @@ 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(); + + // 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, @@ -263,17 +307,19 @@ void _reportValidationResults(OperationContext* opCtx, // Report detailed index validation results gathered when using {full: true} for validated // indexes. - for (const auto& index : validateState->getIndexes()) { - const std::string indexName = index->descriptor()->indexName(); - auto& indexResultsMap = results->indexResultsMap; - if (indexResultsMap.find(indexName) == indexResultsMap.end()) { - continue; - } - - auto& vr = indexResultsMap.at(indexName); - + int nIndexes = results->indexResultsMap.size(); + for (const auto& [indexName, vr] : results->indexResultsMap) { if (!vr.valid) { results->valid = false; + _printIndexSpec(validateState, indexName); + } + + 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; } BSONObjBuilder bob(indexDetails.subobjStart(indexName)); @@ -294,7 +340,7 @@ void _reportValidationResults(OperationContext* opCtx, results->errors.insert(results->errors.end(), vr.errors.begin(), vr.errors.end()); } - output->append("nIndexes", static_cast<int>(validateState->getIndexes().size())); + output->append("nIndexes", nIndexes); output->append("keysPerIndex", keysPerIndex.done()); output->append("indexDetails", indexDetails.done()); } @@ -304,6 +350,7 @@ 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", @@ -410,7 +457,10 @@ void _validateCatalogEntry(OperationContext* opCtx, } const auto& indexCatalog = collection->getIndexCatalog(); - auto indexIt = indexCatalog->getIndexIterator(opCtx, /*includeUnfinishedIndexes=*/true); + auto indexIt = indexCatalog->getIndexIterator(opCtx, + IndexCatalog::InclusionPolicy::kReady | + IndexCatalog::InclusionPolicy::kUnfinished | + IndexCatalog::InclusionPolicy::kFrozen); while (indexIt->more()) { const IndexCatalogEntry* indexEntry = indexIt->next(); @@ -561,12 +611,12 @@ Status validate(OperationContext* opCtx, RepairMode repairMode, ValidateResults* results, BSONObjBuilder* output, - bool turnOnExtraLoggingForTest) { + bool logDiagnostics) { 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, turnOnExtraLoggingForTest); + ValidateState validateState(opCtx, nss, mode, repairMode, logDiagnostics); const auto replCoord = repl::ReplicationCoordinator::get(opCtx); // Check whether we are allowed to read from this node after acquiring our locks. If we are |
