summaryrefslogtreecommitdiff
path: root/src/mongo/db/catalog/validate_adaptor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/catalog/validate_adaptor.cpp')
-rw-r--r--src/mongo/db/catalog/validate_adaptor.cpp67
1 files changed, 55 insertions, 12 deletions
diff --git a/src/mongo/db/catalog/validate_adaptor.cpp b/src/mongo/db/catalog/validate_adaptor.cpp
index acbef39ba9c..34d2cb042fd 100644
--- a/src/mongo/db/catalog/validate_adaptor.cpp
+++ b/src/mongo/db/catalog/validate_adaptor.cpp
@@ -41,7 +41,7 @@
#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/catalog/index_consistency.h"
#include "mongo/db/catalog/throttle_cursor.h"
-#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/concurrency/exception_util.h"
#include "mongo/db/curop.h"
#include "mongo/db/index/index_access_method.h"
#include "mongo/db/index/index_descriptor.h"
@@ -63,6 +63,7 @@ namespace mongo {
namespace {
MONGO_FAIL_POINT_DEFINE(crashOnMultikeyValidateFailure);
+MONGO_FAIL_POINT_DEFINE(failIndexKeyOrdering);
// Set limit for size of corrupted records that will be reported.
const long long kMaxErrorSizeBytes = 1 * 1024 * 1024;
@@ -129,6 +130,24 @@ void schemaValidationFailed(CollectionValidation::ValidateState* state,
}
}
+
+BSONObj rehydrateKey(const BSONObj& keyPattern, const BSONObj& indexKey) {
+ // We need to rehydrate the indexKey for improved readability.
+ // {"": ObjectId(...)} -> {"_id": ObjectId(...)}
+ auto keysIt = keyPattern.begin();
+ auto valuesIt = indexKey.begin();
+
+ BSONObjBuilder b;
+ while (keysIt != keyPattern.end()) {
+ // keysIt and valuesIt must have the same number of elements.
+ invariant(valuesIt != indexKey.end());
+ b.appendAs(*valuesIt, keysIt->fieldName());
+ keysIt++;
+ valuesIt++;
+ }
+
+ return b.obj();
+}
} // namespace
Status ValidateAdaptor::validateRecord(OperationContext* opCtx,
@@ -143,7 +162,7 @@ Status ValidateAdaptor::validateRecord(OperationContext* opCtx,
BSONObj recordBson = record.toBson();
*dataSize = recordBson.objsize();
- if (MONGO_unlikely(_validateState->extraLoggingForTest())) {
+ if (MONGO_unlikely(_validateState->logDiagnostics())) {
LOGV2(4666601, "[validate]", "recordId"_attr = recordId, "recordData"_attr = recordBson);
}
@@ -187,6 +206,26 @@ Status ValidateAdaptor::validateRecord(OperationContext* opCtx,
{multikeyMetadataKeys->begin(), multikeyMetadataKeys->end()},
*documentMultikeyPaths);
+ auto printMultikeyMetadata = [&]() {
+ LOGV2(7556100,
+ "Index is not multikey but document has multikey data",
+ "indexName"_attr = descriptor->indexName(),
+ "recordId"_attr = recordId,
+ "record"_attr = redact(recordBson));
+ for (auto& key : *documentKeySet) {
+ auto indexKey = KeyString::toBsonSafe(key.getBuffer(),
+ key.getSize(),
+ iam->getSortedDataInterface()->getOrdering(),
+ key.getTypeBits());
+ const BSONObj rehydratedKey = rehydrateKey(descriptor->keyPattern(), indexKey);
+ LOGV2(7556101,
+ "Index key for document with multikey inconsistency",
+ "indexName"_attr = descriptor->indexName(),
+ "recordId"_attr = recordId,
+ "indexKey"_attr = redact(rehydratedKey));
+ }
+ };
+
if (!index->isMultikey(opCtx, coll) && shouldBeMultikey) {
if (_validateState->fixErrors()) {
writeConflictRetry(opCtx, "setIndexAsMultikey", coll->ns().ns(), [&] {
@@ -204,10 +243,17 @@ Status ValidateAdaptor::validateRecord(OperationContext* opCtx,
<< " set to multikey.");
results->repaired = true;
} else {
+ printMultikeyMetadata();
+
auto& curRecordResults = (results->indexResultsMap)[descriptor->indexName()];
- std::string msg = str::stream() << "Index " << descriptor->indexName()
- << " is not multikey but has more than one"
- << " key in document " << recordId;
+ const std::string msg = fmt::format(
+ "Index {} is not multikey but document with RecordId({}) and {} has multikey "
+ "data, "
+ "{} key(s)",
+ descriptor->indexName(),
+ recordId.toString(),
+ recordBson.getField("_id").toString(),
+ documentKeySet->size());
curRecordResults.errors.push_back(msg);
curRecordResults.valid = false;
if (crashOnMultikeyValidateFailure.shouldFail()) {
@@ -235,6 +281,8 @@ Status ValidateAdaptor::validateRecord(OperationContext* opCtx,
<< " multikey paths updated.");
results->repaired = true;
} else {
+ printMultikeyMetadata();
+
std::string msg = str::stream()
<< "Index " << descriptor->indexName()
<< " multikey paths do not cover a document. RecordId: " << recordId;
@@ -267,7 +315,7 @@ Status ValidateAdaptor::validateRecord(OperationContext* opCtx,
for (const auto& keyString : *documentKeySet) {
try {
_totalIndexKeys++;
- _indexConsistency->addDocKey(opCtx, keyString, &indexInfo, recordId);
+ _indexConsistency->addDocKey(opCtx, keyString, &indexInfo, recordId, results);
} catch (...) {
return exceptionToStatus();
}
@@ -288,7 +336,7 @@ void _validateKeyOrder(OperationContext* opCtx,
// KeyStrings will be in strictly increasing order because all keys are sorted and they are in
// the format (Key, RID), and all RecordIDs are unique.
- if (currKey.compare(prevKey) <= 0) {
+ if (currKey.compare(prevKey) <= 0 || MONGO_unlikely(failIndexKeyOrdering.shouldFail())) {
if (results && results->valid) {
results->errors.push_back(str::stream()
<< "index '" << descriptor->indexName()
@@ -570,11 +618,6 @@ void ValidateAdaptor::traverseRecordStore(OperationContext* opCtx,
size_t validatedSize = 0;
Status status = validateRecord(opCtx, record->id, record->data, &validatedSize, results);
- // RecordStores are required to return records in RecordId order.
- if (prevRecordId.isValid()) {
- invariant(prevRecordId < record->id);
- }
-
// validatedSize = dataSize is not a general requirement as some storage engines may use
// padding, but we still require that they return the unpadded record data.
if (!status.isOK() || validatedSize != static_cast<size_t>(dataSize)) {