summaryrefslogtreecommitdiff
path: root/src/mongo/db/repl/oplog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/repl/oplog.cpp')
-rw-r--r--src/mongo/db/repl/oplog.cpp97
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,