diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
| commit | 4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch) | |
| tree | 1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/repl/oplog.cpp | |
| parent | aa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff) | |
| parent | 8f0827553e09872941945a093b647a4211a9db7f (diff) | |
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0'
with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/repl/oplog.cpp')
| -rw-r--r-- | src/mongo/db/repl/oplog.cpp | 264 |
1 files changed, 49 insertions, 215 deletions
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 9b750af05ed..453c17c6688 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -45,7 +45,6 @@ #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/privilege.h" -#include "mongo/db/catalog/backwards_compatible_collection_options_util.h" #include "mongo/db/catalog/capped_utils.h" #include "mongo/db/catalog/coll_mod.h" #include "mongo/db/catalog/collection.h" @@ -56,7 +55,6 @@ #include "mongo/db/catalog/drop_collection.h" #include "mongo/db/catalog/drop_database.h" #include "mongo/db/catalog/drop_indexes.h" -#include "mongo/db/catalog/health_log_interface.h" #include "mongo/db/catalog/import_collection_oplog_entry_gen.h" #include "mongo/db/catalog/local_oplog_info.h" #include "mongo/db/catalog/multi_index_block.h" @@ -64,9 +62,8 @@ #include "mongo/db/client.h" #include "mongo/db/coll_mod_gen.h" #include "mongo/db/commands.h" -#include "mongo/db/commands/create_gen.h" #include "mongo/db/commands/feature_compatibility_version_parser.h" -#include "mongo/db/concurrency/exception_util.h" +#include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/db_raii.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/dbhelpers.h" @@ -120,7 +117,6 @@ using std::string; using std::stringstream; using std::unique_ptr; using std::vector; -using namespace std::string_literals; using IndexVersion = IndexDescriptor::IndexVersion; @@ -293,6 +289,23 @@ void writeToImageCollection(OperationContext* opCtx, DisableDocumentValidation documentValidationDisabler( opCtx, DocumentValidationSettings::kDisableInternalValidation); + BSONObj existingImageEntryBson; + Helpers::findOne(opCtx, + autoColl.getCollection(), + BSON("_id" << imageEntry.get_id().toBSON() << "ts" << imageEntry.getTs()), + existingImageEntryBson); + if (!existingImageEntryBson.isEmpty()) { + auto existingImageEntry = repl::ImageEntry::parse( + IDLParserErrorContext("writeToImageCollection"), existingImageEntryBson); + uassert( + 6652600, + str::stream() + << "Found an existing findAndModify image entry with unexpected content. Found: " + << existingImageEntry.toBSON() << ". Expected: " << imageEntry.toBSON(), + existingImageEntry.toBSON().woCompare(imageEntry.toBSON()) == 0); + return; + } + UpdateRequest request; request.setNamespaceString(NamespaceString::kConfigImagesNamespace); request.setQuery( @@ -793,21 +806,6 @@ NamespaceString extractNsFromUUIDorNs(OperationContext* opCtx, return ui ? extractNsFromUUID(opCtx, ui.get()) : extractNs(ns.db(), cmd); } -BSONObj getObjWithSanitizedStorageEngineOptions(OperationContext* opCtx, const BSONObj& cmd) { - static_assert( - CreateCommand::kStorageEngineFieldName == IndexDescriptor::kStorageEngineFieldName, - "Expected storage engine options field to be the same for collections and indexes."); - - if (auto storageEngineElem = cmd[IndexDescriptor::kStorageEngineFieldName]) { - auto storageEngine = opCtx->getServiceContext()->getStorageEngine(); - auto engineObj = storageEngineElem.embeddedObject(); - auto sanitizedObj = - storageEngine->getSanitizedStorageOptionsForSecondaryReplication(engineObj); - return cmd.addFields(BSON(IndexDescriptor::kStorageEngineFieldName << sanitizedObj)); - } - return cmd; -} - using OpApplyFn = std::function<Status( OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode)>; @@ -832,9 +830,7 @@ const StringMap<ApplyOpMetadata> kOpsMap = { {"create", {[](OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode) -> Status { const auto& ui = entry.getUuid(); - // Sanitize storage engine options to remove options which might not apply to this node. - // See SERVER-68122. - const auto cmd = getObjWithSanitizedStorageEngineOptions(opCtx, entry.getObject()); + const auto& cmd = entry.getObject(); const NamespaceString nss(extractNs(entry.getNss().db(), cmd)); // Mode SECONDARY steady state replication should not allow create collection to rename an @@ -844,23 +840,6 @@ const StringMap<ApplyOpMetadata> kOpsMap = { // complete. const bool allowRenameOutOfTheWay = (mode != repl::OplogApplication::Mode::kSecondary); - // Check whether there is an open but empty database where the name conflicts with the new - // collection's database name. It is possible for a secondary's in-memory database state - // to diverge from the primary's, if the primary rolls back the dropDatabase oplog entry - // after closing its own in-memory database state. In this case, the primary may accept - // creating a new database with a conflicting name to what the secondary still has open. - // It is okay to simply close the empty database on the secondary in this case. - auto duplicates = DatabaseHolder::get(opCtx)->getNamesWithConflictingCasing( - TenantDatabaseName(boost::none, nss.db())); - if (duplicates.size() == 1) { - auto dupDatabaseIt = duplicates.begin(); - if (CollectionCatalog::get(opCtx) - ->getAllCollectionUUIDsFromDb(*dupDatabaseIt) - .size() == 0) { - fassert(7727801, dropDatabaseForApplyOps(opCtx, dupDatabaseIt->dbName()).isOK()); - } - } - Lock::DBLock dbLock(opCtx, nss.db(), MODE_IX); if (auto idIndexElem = cmd["idIndex"]) { // Remove "idIndex" field from command. @@ -895,10 +874,7 @@ const StringMap<ApplyOpMetadata> kOpsMap = { {ErrorCodes::NamespaceExists}}}, {"createIndexes", {[](OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode) -> Status { - // Sanitize storage engine options to remove options which might not apply to this node. - // See SERVER-68122. - const auto cmd = getObjWithSanitizedStorageEngineOptions(opCtx, entry.getObject()); - + const auto& cmd = entry.getObject(); if (OplogApplication::Mode::kApplyOpsCmd == mode) { return {ErrorCodes::CommandNotSupported, "The createIndexes operation is not supported in applyOps mode"}; @@ -933,12 +909,6 @@ const StringMap<ApplyOpMetadata> kOpsMap = { "Error parsing 'startIndexBuild' oplog entry"); } - // Sanitize storage engine options to remove options which might not apply to this node. - // See SERVER-68122. - for (auto& spec : swOplogEntry.getValue().indexSpecs) { - spec = getObjWithSanitizedStorageEngineOptions(opCtx, spec); - } - IndexBuildsCoordinator::ApplicationMode applicationMode = IndexBuildsCoordinator::ApplicationMode::kNormal; if (mode == OplogApplication::Mode::kInitialSync) { @@ -989,8 +959,7 @@ const StringMap<ApplyOpMetadata> kOpsMap = { {ErrorCodes::NamespaceNotFound}}}, {"collMod", {[](OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode) -> Status { - const auto cmd = - backwards_compatible_collection_options::parseCollModCmdFromOplogEntry(entry); + const auto& cmd = entry.getObject(); auto opMsg = OpMsgRequest::fromDBAndBody(entry.getNss().db(), cmd); auto collModCmd = CollMod::parse(IDLParserErrorContext("collModOplogEntry"), opMsg); const auto nssOrUUID([&collModCmd, &entry, mode]() -> NamespaceStringOrUUID { @@ -1141,8 +1110,6 @@ void writeChangeStreamPreImage(OperationContext* opCtx, constexpr StringData OplogApplication::kInitialSyncOplogApplicationMode; constexpr StringData OplogApplication::kRecoveringOplogApplicationMode; -constexpr StringData OplogApplication::kStableRecoveringOplogApplicationMode; -constexpr StringData OplogApplication::kUnstableRecoveringOplogApplicationMode; constexpr StringData OplogApplication::kSecondaryOplogApplicationMode; constexpr StringData OplogApplication::kApplyOpsCmdOplogApplicationMode; @@ -1150,10 +1117,8 @@ StringData OplogApplication::modeToString(OplogApplication::Mode mode) { switch (mode) { case OplogApplication::Mode::kInitialSync: return OplogApplication::kInitialSyncOplogApplicationMode; - case OplogApplication::Mode::kUnstableRecovering: - return OplogApplication::kUnstableRecoveringOplogApplicationMode; - case OplogApplication::Mode::kStableRecovering: - return OplogApplication::kStableRecoveringOplogApplicationMode; + case OplogApplication::Mode::kRecovering: + return OplogApplication::kRecoveringOplogApplicationMode; case OplogApplication::Mode::kSecondary: return OplogApplication::kSecondaryOplogApplicationMode; case OplogApplication::Mode::kApplyOpsCmd: @@ -1166,9 +1131,7 @@ StatusWith<OplogApplication::Mode> OplogApplication::parseMode(const std::string if (mode == OplogApplication::kInitialSyncOplogApplicationMode) { return OplogApplication::Mode::kInitialSync; } else if (mode == OplogApplication::kRecoveringOplogApplicationMode) { - // This only being used in applyOps command which is controlled by the client, so it should - // be unstable. - return OplogApplication::Mode::kUnstableRecovering; + return OplogApplication::Mode::kRecovering; } else if (mode == OplogApplication::kSecondaryOplogApplicationMode) { return OplogApplication::Mode::kSecondary; } else if (mode == OplogApplication::kApplyOpsCmdOplogApplicationMode) { @@ -1180,76 +1143,6 @@ StatusWith<OplogApplication::Mode> OplogApplication::parseMode(const std::string MONGO_UNREACHABLE; } -void OplogApplication::checkOnOplogFailureForRecovery(OperationContext* opCtx, - const mongo::NamespaceString& nss, - const mongo::BSONObj& oplogEntry, - const std::string& errorMsg) { - const bool isReplicaSet = - repl::ReplicationCoordinator::get(opCtx->getServiceContext())->getReplicationMode() == - repl::ReplicationCoordinator::modeReplSet; - // Relax the constraints of oplog application if the node is not a replica set member or the - // node is in the middle of a backup and restore process. - if (!isReplicaSet || storageGlobalParams.restore) { - return; - } - - // During the recovery process, certain configuration collections such as - // 'config.image_collections' are handled differently, which may result in encountering oplog - // application failures in common scenarios, and therefore assert statements are not used. - if (nss.isConfigDB()) { - LOGV2_DEBUG( - 5415002, - 1, - "Error applying operation while recovering from stable checkpoint. This is related to " - "one of the configuration collections so this error might be benign.", - "oplogEntry"_attr = oplogEntry, - "error"_attr = errorMsg); - } else if (getTestCommandsEnabled()) { - // Only fassert in test environment. - LOGV2_FATAL(5415000, - "Error applying operation while recovering from stable " - "checkpoint. This can lead to data corruption.", - "oplogEntry"_attr = oplogEntry, - "error"_attr = errorMsg); - } else { - LOGV2_WARNING(5415001, - "Error applying operation while recovering from stable " - "checkpoint. This can lead to data corruption.", - "oplogEntry"_attr = oplogEntry, - "error"_attr = errorMsg); - } -} - -// Logger for oplog constraint violations. -OplogConstraintViolationLogger* oplogConstraintViolationLogger; - -MONGO_INITIALIZER(CreateOplogConstraintViolationLogger)(InitializerContext* context) { - oplogConstraintViolationLogger = new OplogConstraintViolationLogger(); -} - -void logOplogConstraintViolation(OperationContext* opCtx, - const NamespaceString& nss, - OplogConstraintViolationEnum type, - const std::string& operation, - const BSONObj& opObj, - boost::optional<Status> status) { - // Log the violation. - oplogConstraintViolationLogger->logViolationIfReady(type, opObj, status); - - // Write a new entry to the health log. - HealthLogEntry entry; - entry.setNss(nss); - entry.setTimestamp(Date_t::now()); - // Oplog constraint violations should always be marked as warning. - entry.setSeverity(SeverityEnum::Warning); - entry.setScope(ScopeEnum::Document); - entry.setMsg(toString(type)); - entry.setOperation(operation); - entry.setData(opObj); - - HealthLogInterface::get(opCtx->getServiceContext())->log(entry); -} - // @return failure status if an update should have happened and the document DNE. // See replset initial sync code. Status applyOperation_inlock(OperationContext* opCtx, @@ -1286,22 +1179,11 @@ Status applyOperation_inlock(OperationContext* opCtx, return Status::OK(); } - const bool inStableRecovery = mode == OplogApplication::Mode::kStableRecovering; NamespaceString requestNss; CollectionPtr collection = nullptr; if (auto uuid = op.getUuid()) { auto catalog = CollectionCatalog::get(opCtx); collection = catalog->lookupCollectionByUUID(opCtx, uuid.get()); - if (!collection && inStableRecovery) { - repl::OplogApplication::checkOnOplogFailureForRecovery( - opCtx, - op.getNss(), - redact(op.toBSONForLogging()), - str::stream() - << "(NamespaceNotFound): Failed to apply operation due to missing collection (" - << uuid.value() << ")"); - } - uassert(ErrorCodes::NamespaceNotFound, str::stream() << "Failed to apply operation due to missing collection (" << uuid.get() << "): " << redact(opOrGroupedInserts.toBSON()), @@ -1369,7 +1251,7 @@ Status applyOperation_inlock(OperationContext* opCtx, case ReplicationCoordinator::modeNone: { // Only assign timestamps on standalones during replication recovery when // started with the 'recoverFromOplogAsStandalone' flag. - return OplogApplication::inRecovering(mode); + return mode == OplogApplication::Mode::kRecovering; } } } @@ -1388,7 +1270,8 @@ Status applyOperation_inlock(OperationContext* opCtx, // correct pre-image for them. return collection && collection->isChangeStreamPreAndPostImagesEnabled() && isDataConsistent && - (OplogApplication::inRecovering(mode) || mode == OplogApplication::Mode::kSecondary) && + (mode == OplogApplication::Mode::kRecovering || + mode == OplogApplication::Mode::kSecondary) && !op.getFromMigrate().get_value_or(false) && !requestNss.isTemporaryReshardingCollection(); }; @@ -1528,23 +1411,10 @@ Status applyOperation_inlock(OperationContext* opCtx, return status; } if (mode == OplogApplication::Mode::kSecondary) { - const auto& opObj = redact(op.toBSONForLogging()); - opCounters->gotInsertOnExistingDoc(); - logOplogConstraintViolation( - opCtx, - op.getNss(), - OplogConstraintViolationEnum::kInsertOnExistingDoc, - "insert", - opObj, - boost::none /* status */); - if (oplogApplicationEnforcesSteadyStateConstraints) { return status; } - } else if (inStableRecovery) { - repl::OplogApplication::checkOnOplogFailureForRecovery( - opCtx, op.getNss(), redact(op.toBSONForLogging()), redact(status)); } // Continue to the next block to retry the operation as an upsert. needToDoUpsert = true; @@ -1619,13 +1489,13 @@ Status applyOperation_inlock(OperationContext* opCtx, request.setNamespaceString(requestNss); request.setQuery(updateCriteria); // If we are in steady state and the update is on a timeseries bucket collection, we can - // enable some optimizations in diff application. In some cases, like during tenant - // migration or $_internalApplyOplogUpdate update, we can for some reason generate - // entries for timeseries bucket collections which still rely on the idempotency - // guarantee, which then means we shouldn't apply these optimizations. + // enable some optimizations in diff application. In some cases, during tenant + // migration, we can for some reason generate entries for timeseries bucket collections + // which still rely on the idempotency guarantee, which then means we shouldn't apply + // these optimizations. write_ops::UpdateModification::DiffOptions options; if (mode == OplogApplication::Mode::kSecondary && collection->getTimeseriesOptions() && - !op.getCheckExistenceForDiffInsert() && !op.getFromTenantMigration()) { + !op.getFromTenantMigration()) { options.mustCheckExistenceForInsertOperations = false; } auto updateMod = write_ops::UpdateModification::parseFromOplogEntry(o, options); @@ -1762,15 +1632,10 @@ Status applyOperation_inlock(OperationContext* opCtx, !ur.upsertedId.isEmpty() && !(collection && collection->isCapped())) { // This indicates we upconverted an update to an upsert, and it did indeed // upsert. In steady state mode this is unexpected. - const auto& opObj = redact(op.toBSONForLogging()); - + LOGV2_WARNING(2170001, + "update needed to be converted to upsert", + "op"_attr = redact(op.toBSONForLogging())); opCounters->gotUpdateOnMissingDoc(); - logOplogConstraintViolation(opCtx, - op.getNss(), - OplogConstraintViolationEnum::kUpdateOnMissingDoc, - "update", - opObj, - boost::none /* status */); // We shouldn't be doing upserts in secondary mode when enforcing steady state // constraints. @@ -1807,10 +1672,6 @@ Status applyOperation_inlock(OperationContext* opCtx, }); if (!status.isOK()) { - if (inStableRecovery) { - repl::OplogApplication::checkOnOplogFailureForRecovery( - opCtx, op.getNss(), redact(op.toBSONForLogging()), redact(status)); - } return status; } @@ -1892,22 +1753,16 @@ Status applyOperation_inlock(OperationContext* opCtx, writeChangeStreamPreImage(opCtx, collection, op, *(result.requestedPreImage)); } - if (result.nDeleted == 0 && inStableRecovery) { - repl::OplogApplication::checkOnOplogFailureForRecovery( - opCtx, - op.getNss(), - redact(op.toBSONForLogging()), - !collection ? str::stream() - << "(NamespaceNotFound): Failed to apply operation due " - "to missing collection (" - << requestNss << ")" - : "Applied a delete which did not delete anything."s); - } // It is legal for a delete operation on the pre-images collection to delete zero // documents - pre-image collections are not guaranteed to contain the same set of // documents at all times. if (result.nDeleted == 0 && mode == OplogApplication::Mode::kSecondary && !requestNss.isChangeStreamPreImagesCollection()) { + LOGV2_WARNING(2170002, + "Applied a delete which did not delete anything in steady state " + "replication", + "op"_attr = redact(op.toBSONForLogging())); + // In FCV 4.4, each node is responsible for deleting the excess documents in // capped collections. This implies that capped deletes may not be synchronized // between nodes at times. When upgraded to FCV 5.0, the primary will generate @@ -1920,25 +1775,11 @@ Status applyOperation_inlock(OperationContext* opCtx, // capped collections when oplog application is enforcing steady state // constraints. bool isCapped = false; - const auto& opObj = redact(op.toBSONForLogging()); if (collection) { isCapped = collection->isCapped(); opCounters->gotDeleteWasEmpty(); - logOplogConstraintViolation(opCtx, - op.getNss(), - OplogConstraintViolationEnum::kDeleteWasEmpty, - "delete", - opObj, - boost::none /* status */); } else { opCounters->gotDeleteFromMissingNamespace(); - logOplogConstraintViolation( - opCtx, - op.getNss(), - OplogConstraintViolationEnum::kDeleteOnMissingNs, - "delete", - opObj, - boost::none /* status */); } if (!isCapped) { @@ -2057,7 +1898,7 @@ Status applyCommand_inlock(OperationContext* opCtx, case ReplicationCoordinator::modeNone: { // Only assign timestamps on standalones during replication recovery when // started with 'recoverFromOplogAsStandalone'. - return OplogApplication::inRecovering(mode); + return mode == OplogApplication::Mode::kRecovering; } } MONGO_UNREACHABLE; @@ -2128,17 +1969,12 @@ Status applyCommand_inlock(OperationContext* opCtx, "aborting index build and retrying", logAttrs(ns)); } else { - opCtx->recoveryUnit()->abandonSnapshot(); - auto lockState = opCtx->lockState(); Locker::LockSnapshot lockSnapshot; - bool canSaveState = lockState->canSaveLockState(); - if (canSaveState) { - lockState->saveLockStateAndUnlock(&lockSnapshot); - } + auto locksReleased = lockState->saveLockStateAndUnlock(&lockSnapshot); ScopeGuard guard{[&] { - if (canSaveState) { + if (locksReleased) { invariant(!lockState->isLocked()); lockState->restoreLockState(lockSnapshot); } @@ -2154,6 +1990,7 @@ Status applyCommand_inlock(OperationContext* opCtx, IndexBuildsCoordinator::get(opCtx)->awaitNoIndexBuildInProgressForCollection( opCtx, swUUID.get()); + opCtx->recoveryUnit()->abandonSnapshot(); opCtx->checkForInterrupt(); LOGV2_DEBUG( @@ -2191,15 +2028,12 @@ Status applyCommand_inlock(OperationContext* opCtx, if (mode == OplogApplication::Mode::kSecondary && status.code() != ErrorCodes::IndexNotFound) { - const auto& opObj = redact(entry.toBSONForLogging()); + LOGV2_WARNING(2170000, + "Acceptable error during oplog application", + "db"_attr = nss.db(), + "error"_attr = status, + "oplogEntry"_attr = redact(entry.toBSONForLogging())); opCounters->gotAcceptableErrorInCommand(); - logOplogConstraintViolation( - opCtx, - entry.getNss(), - OplogConstraintViolationEnum::kAcceptableErrorInCommand, - "command", - opObj, - status); } else { LOGV2_DEBUG(51776, 1, |
