diff options
Diffstat (limited to 'src/mongo/db/repl/oplog.cpp')
| -rw-r--r-- | src/mongo/db/repl/oplog.cpp | 97 |
1 files changed, 82 insertions, 15 deletions
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 9edc5f48b4f..3e79a96362e 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -45,6 +45,7 @@ #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" @@ -55,6 +56,7 @@ #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" @@ -987,7 +989,8 @@ const StringMap<ApplyOpMetadata> kOpsMap = { {ErrorCodes::NamespaceNotFound}}}, {"collMod", {[](OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode) -> Status { - const auto& cmd = entry.getObject(); + const auto cmd = + backwards_compatible_collection_options::parseCollModCmdFromOplogEntry(entry); auto opMsg = OpMsgRequest::fromDBAndBody(entry.getNss().db(), cmd); auto collModCmd = CollMod::parse(IDLParserErrorContext("collModOplogEntry"), opMsg); const auto nssOrUUID([&collModCmd, &entry, mode]() -> NamespaceStringOrUUID { @@ -1217,6 +1220,36 @@ void OplogApplication::checkOnOplogFailureForRecovery(OperationContext* opCtx, } } +// 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, @@ -1495,7 +1528,17 @@ 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; } @@ -1719,10 +1762,15 @@ 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. - LOGV2_WARNING(2170001, - "update needed to be converted to upsert", - "op"_attr = redact(op.toBSONForLogging())); + const auto& opObj = 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. @@ -1858,13 +1906,15 @@ Status applyOperation_inlock(OperationContext* opCtx, // 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. + // + // It is also legal for a delete operation on the config.image_collection (used for + // find-and-modify retries) to delete zero documents. Since we do not write updates + // to this collection which are in the same batch as later deletes, a rollback to + // the middle of a batch with both an update and a delete may result in a missing + // document, which may be later deleted. 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())); - + !requestNss.isChangeStreamPreImagesCollection() && + !requestNss.isConfigImagesCollection()) { // 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 @@ -1877,11 +1927,25 @@ 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) { @@ -2134,12 +2198,15 @@ Status applyCommand_inlock(OperationContext* opCtx, if (mode == OplogApplication::Mode::kSecondary && status.code() != ErrorCodes::IndexNotFound) { - LOGV2_WARNING(2170000, - "Acceptable error during oplog application", - "db"_attr = nss.db(), - "error"_attr = status, - "oplogEntry"_attr = redact(entry.toBSONForLogging())); + const auto& opObj = redact(entry.toBSONForLogging()); opCounters->gotAcceptableErrorInCommand(); + logOplogConstraintViolation( + opCtx, + entry.getNss(), + OplogConstraintViolationEnum::kAcceptableErrorInCommand, + "command", + opObj, + status); } else { LOGV2_DEBUG(51776, 1, |
