diff options
Diffstat (limited to 'src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp')
| -rw-r--r-- | src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp | 172 |
1 files changed, 149 insertions, 23 deletions
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp index 09b1603361e..0a498ead248 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp @@ -38,12 +38,16 @@ #include <set> #include "mongo/base/checked_cast.h" +#include "mongo/base/string_data.h" +#include "mongo/db/catalog/health_log.h" +#include "mongo/db/catalog/health_log_gen.h" #include "mongo/db/catalog/index_catalog_entry.h" #include "mongo/db/catalog/validate_results.h" #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/global_settings.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/json.h" +#include "mongo/db/namespace_string.h" #include "mongo/db/repl/repl_settings.h" #include "mongo/db/service_context.h" #include "mongo/db/stats/resource_consumption_metrics.h" @@ -59,6 +63,7 @@ #include "mongo/util/assert_util.h" #include "mongo/util/fail_point.h" #include "mongo/util/hex.h" +#include "mongo/util/stacktrace.h" #include "mongo/util/str.h" #include "mongo/util/testing_proctor.h" @@ -84,8 +89,53 @@ namespace { MONGO_FAIL_POINT_DEFINE(WTCompactIndexEBUSY); MONGO_FAIL_POINT_DEFINE(WTIndexPauseAfterSearchNear); MONGO_FAIL_POINT_DEFINE(WTValidateIndexStructuralDamage); +MONGO_FAIL_POINT_DEFINE(WTIndexUassertDuplicateRecordForKeyOnIdUnindex); static const WiredTigerItem emptyItem(nullptr, 0); + +/** + * Add a data corruption entry to the health log. + */ +void addDataCorruptionEntryToHealthLog(OperationContext* opCtx, + const NamespaceString& nss, + StringData operation, + StringData message, + const BSONObj& key, + StringData indexName, + StringData uri) { + HealthLogEntry entry; + entry.setNss(nss); + entry.setTimestamp(Date_t::now()); + entry.setSeverity(SeverityEnum::Error); + entry.setScope(ScopeEnum::Index); + entry.setOperation(operation); + entry.setMsg(message); + + BSONObjBuilder bob; + bob.append("key", key); + bob.append("indexName", indexName); + bob.append("uri", uri); + bob.appendElements(getStackTrace().getBSONRepresentation()); + entry.setData(bob.obj()); + + HealthLog::get(opCtx)->log(entry); +} + +/** + * Returns the logv2::LogOptions controlling the behaviour after logging a data corruption + * error. When the TestingProctor is enabled we will fatally assert. When the testing proctor is + * disabled or when 'forceUassert' is specified (for instance because a failpoint is enabled), + * we should log and throw DataCorruptionDetected. + */ +logv2::LogOptions getLogOptionsForDataCorruption(RecoveryUnit& ru, bool forceUassert = false) { + if (ru.getDataCorruptionDetectionMode() == DataCorruptionDetectionMode::kThrow || + MONGO_unlikely(forceUassert)) { + return logv2::LogOptions{logv2::UserAssertAfterLog(ErrorCodes::DataCorruptionDetected)}; + } else { + return logv2::LogOptions(logv2::LogComponent::kAutomaticDetermination); + } +} + } // namespace void WiredTigerIndex::setKey(WT_CURSOR* cursor, const WT_ITEM* item) { @@ -240,7 +290,7 @@ WiredTigerIndex::WiredTigerIndex(OperationContext* ctx, bool isLogged, bool isReadOnly) : SortedDataInterface(ident, - _handleVersionInfo(ctx, uri, desc, isLogged, isReadOnly), + _handleVersionInfo(ctx, uri, ident, desc, isLogged, isReadOnly), Ordering::make(desc->keyPattern()), rsKeyFormat), _uri(uri), @@ -711,8 +761,45 @@ StatusWith<bool> WiredTigerIndex::_checkDups(OperationContext* opCtx, _collation); } +void WiredTigerIndex::_repairDataFormatVersion(OperationContext* opCtx, + const std::string& uri, + StringData ident, + const IndexDescriptor* desc) { + auto indexVersion = desc->version(); + auto isIndexVersion1 = indexVersion == IndexDescriptor::IndexVersion::kV1; + auto isIndexVersion2 = indexVersion == IndexDescriptor::IndexVersion::kV2; + auto isDataFormat6 = _dataFormatVersion == kDataFormatV1KeyStringV0IndexVersionV1; + auto isDataFormat8 = _dataFormatVersion == kDataFormatV2KeyStringV1IndexVersionV2; + auto isDataFormat13 = _dataFormatVersion == kDataFormatV5KeyStringV0UniqueIndexVersionV1; + auto isDataFormat14 = _dataFormatVersion == kDataFormatV6KeyStringV1UniqueIndexVersionV2; + // Only fixes the index data format when it could be from an edge case when converting the + // uniqueness of the index. Specifically: + // * The index is a secondary unique index, but the data format version is 6 (v1) or 8 (v2). + // * The index is a non-unique index, but the data format version is 13 (v1) or 14 (v2). + if ((!desc->isIdIndex() && desc->unique() && + ((isIndexVersion1 && isDataFormat6) || (isIndexVersion2 && isDataFormat8))) || + (!desc->unique() && + ((isIndexVersion1 && isDataFormat13) || (isIndexVersion2 && isDataFormat14)))) { + auto engine = opCtx->getServiceContext()->getStorageEngine(); + engine->getEngine()->alterIdentMetadata( + opCtx, ident, desc, /* isForceUpdateMetadata */ false); + auto prevVersion = _dataFormatVersion; + // The updated data format is guaranteed to be within the supported version range. + _dataFormatVersion = WiredTigerUtil::checkApplicationMetadataFormatVersion( + opCtx, uri, kMinimumIndexVersion, kMaximumIndexVersion) + .getValue(); + LOGV2_WARNING(6818600, + "Fixing index metadata data format version", + "namespace"_attr = desc->getEntry()->getNSSFromCatalog(opCtx), + "indexName"_attr = desc->indexName(), + "prevVersion"_attr = prevVersion, + "newVersion"_attr = _dataFormatVersion); + } +} + KeyString::Version WiredTigerIndex::_handleVersionInfo(OperationContext* ctx, const std::string& uri, + StringData ident, const IndexDescriptor* desc, bool isLogged, bool isReadOnly) { @@ -730,6 +817,8 @@ KeyString::Version WiredTigerIndex::_handleVersionInfo(OperationContext* ctx, } _dataFormatVersion = version.getValue(); + _repairDataFormatVersion(ctx, uri, ident, desc); + if (!desc->isIdIndex() && desc->unique() && (_dataFormatVersion < kDataFormatV3KeyStringV0UniqueIndexVersionV1 || _dataFormatVersion > kDataFormatV6KeyStringV1UniqueIndexVersionV2)) { @@ -1510,14 +1599,24 @@ private: _typeBits.resetFromBuffer(&br); if (!br.atEof()) { - LOGV2_FATAL(28608, - "Unique index cursor seeing multiple records for key {key} in index " - "{index} ({uri}) belonging to collection {collection}", - "Unique index cursor seeing multiple records for key in index", - "key"_attr = redact(curr(kWantKey)->key), - "index"_attr = _idx.indexName(), - "uri"_attr = _idx.uri(), - "collection"_attr = _idx.getCollectionNamespace(_opCtx)); + const auto bsonKey = redact(curr(kWantKey)->key); + const auto collectionNamespace = _idx.getCollectionNamespace(_opCtx); + addDataCorruptionEntryToHealthLog( + _opCtx, + collectionNamespace, + "WiredTigerIndexUniqueCursor::_updateIdAndTypeBitsFromValue", + "Unique index cursor seeing multiple records for key in index", + bsonKey, + _idx.indexName(), + _idx.uri()); + + LOGV2_ERROR_OPTIONS(7623202, + getLogOptionsForDataCorruption(*_opCtx->recoveryUnit()), + "Unique index cursor seeing multiple records for key in index", + "key"_attr = bsonKey, + "index"_attr = _idx.indexName(), + "uri"_attr = _idx.uri(), + logAttrs(collectionNamespace)); } } }; @@ -1545,12 +1644,25 @@ public: _typeBits.resetFromBuffer(&br); if (!br.atEof()) { - LOGV2_FATAL(5176200, - "Index cursor seeing multiple records for key in _id index", - "key"_attr = redact(curr(kWantKey)->key), - "index"_attr = _idx.indexName(), - "uri"_attr = _idx.uri(), - "collection"_attr = _idx.getCollectionNamespace(_opCtx)); + const auto bsonKey = redact(curr(kWantKey)->key); + const auto collectionNamespace = _idx.getCollectionNamespace(_opCtx); + + addDataCorruptionEntryToHealthLog( + _opCtx, + collectionNamespace, + "WiredTigerIdIndexCursor::updateIdAndTypeBits", + "Index cursor seeing multiple records for key in _id index", + bsonKey, + _idx.indexName(), + _idx.uri()); + + LOGV2_ERROR_OPTIONS(5176200, + getLogOptionsForDataCorruption(*_opCtx->recoveryUnit()), + "Index cursor seeing multiple records for key in _id index", + "key"_attr = bsonKey, + "index"_attr = _idx.indexName(), + "uri"_attr = _idx.uri(), + logAttrs(collectionNamespace)); } } }; @@ -1773,9 +1885,11 @@ void WiredTigerIdIndex::_unindex(OperationContext* opCtx, WiredTigerItem keyItem(keyString.getBuffer(), sizeWithoutRecordId); setKey(c, keyItem.Get()); + const auto failWithDataCorruptionForTest = + WTIndexUassertDuplicateRecordForKeyOnIdUnindex.shouldFail(); // On the _id index, the RecordId is stored in the value of the index entry. If the dupsAllowed // flag is not set, we blindly delete using only the key without checking the RecordId. - if (!dupsAllowed) { + if (!dupsAllowed && MONGO_likely(!failWithDataCorruptionForTest)) { int ret = WT_OP_CHECK(wiredTigerCursorRemove(opCtx, c)); if (ret == WT_NOTFOUND) { return; @@ -1807,14 +1921,26 @@ void WiredTigerIdIndex::_unindex(OperationContext* opCtx, RecordId idInIndex = KeyString::decodeRecordIdLong(&br); KeyString::TypeBits typeBits = KeyString::TypeBits::fromBuffer(getKeyStringVersion(), &br); - if (!br.atEof()) { + if (!br.atEof() || MONGO_unlikely(failWithDataCorruptionForTest)) { auto bsonKey = KeyString::toBson(keyString, _ordering); - LOGV2_FATAL(5176201, - "Un-index seeing multiple records for key", - "key"_attr = bsonKey, - "index"_attr = _desc->indexName(), - "uri"_attr = _uri, - "collection"_attr = getCollectionNamespace(opCtx)); + const auto collectionNamespace = getCollectionNamespace(opCtx); + + addDataCorruptionEntryToHealthLog(opCtx, + collectionNamespace, + "WiredTigerIdIndex::_unindex", + "Un-index seeing multiple records for key", + bsonKey, + _indexName, + _uri); + + LOGV2_ERROR_OPTIONS( + 5176201, + getLogOptionsForDataCorruption(*opCtx->recoveryUnit(), failWithDataCorruptionForTest), + "Un-index seeing multiple records for key", + "key"_attr = bsonKey, + "index"_attr = _indexName, + "uri"_attr = _uri, + logAttrs(collectionNamespace)); } // The RecordId matches, so remove the entry. |
