summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/commands.h16
-rw-r--r--src/mongo/db/commands/count_cmd.cpp4
-rw-r--r--src/mongo/db/commands/distinct.cpp4
-rw-r--r--src/mongo/db/commands/find_cmd.cpp4
-rw-r--r--src/mongo/db/commands/getmore_cmd.cpp4
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp6
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp3
-rw-r--r--src/mongo/db/read_concern.h10
-rw-r--r--src/mongo/db/read_concern_mongod.cpp18
-rw-r--r--src/mongo/db/s/migration_source_manager.cpp3
-rw-r--r--src/mongo/db/service_entry_point_mongod.cpp6
-rw-r--r--src/mongo/embedded/read_concern_embedded.cpp2
-rw-r--r--src/mongo/embedded/service_entry_point_embedded.cpp5
13 files changed, 65 insertions, 20 deletions
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 72a6f1afda9..b11daed69f0 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -491,6 +491,14 @@ public:
}
/**
+ * Returns true if a command may be able to safely ignore prepare conflicts. Only commands that
+ * can guarantee they will only perform reads may ignore prepare conflicts.
+ */
+ virtual bool canIgnorePrepareConflicts() const {
+ return false;
+ }
+
+ /**
* Returns true if this command invocation is allowed to utilize "speculative" majority reads to
* service 'majority' read concern requests. This allows a query to satisfy a 'majority' read
* without storage engine support for reading from a historical snapshot.
@@ -624,6 +632,14 @@ public:
return true;
}
+ /**
+ * Returns true if a command may be able to safely ignore prepare conflicts. Only commands that
+ * can guarantee they will only perform reads may ignore prepare conflicts.
+ */
+ virtual bool canIgnorePrepareConflicts() const {
+ return false;
+ }
+
private:
std::unique_ptr<CommandInvocation> parse(OperationContext* opCtx,
const OpMsgRequest& request) final;
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index 312f8b44b13..360fb2f3128 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -72,6 +72,10 @@ public:
return false;
}
+ bool canIgnorePrepareConflicts() const override {
+ return true;
+ }
+
AllowedOnSecondary secondaryAllowed(ServiceContext* serviceContext) const override {
return Command::AllowedOnSecondary::kOptIn;
}
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index cf5b0572027..9370da8bd73 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -78,6 +78,10 @@ public:
return false;
}
+ bool canIgnorePrepareConflicts() const override {
+ return true;
+ }
+
bool supportsReadConcern(const std::string& dbName,
const BSONObj& cmdObj,
repl::ReadConcernLevel level) const override {
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index ddde7ac8972..2c17b974c5c 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -125,6 +125,10 @@ public:
return true;
}
+ bool canIgnorePrepareConflicts() const override {
+ return true;
+ }
+
bool allowsSpeculativeMajorityReads() const override {
// Find queries are only allowed to use speculative behavior if the 'allowsSpeculative'
// flag is passed. The find command will check for this flag internally and fail if
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp
index 3fbad104b55..e0b710c5fcd 100644
--- a/src/mongo/db/commands/getmore_cmd.cpp
+++ b/src/mongo/db/commands/getmore_cmd.cpp
@@ -200,6 +200,10 @@ public:
return false;
}
+ bool canIgnorePrepareConflicts() const override {
+ return true;
+ }
+
NamespaceString ns() const override {
return _request.nss;
}
diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp
index dcb891c8344..65703ffec1d 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -76,6 +76,12 @@ public:
return Pipeline::aggSupportsWriteConcern(this->_request.body);
}
+ bool canIgnorePrepareConflicts() const override {
+ // Aggregate is a special case for prepare conflicts. It may do writes to an output
+ // collection, but it enables enforcement of prepare conflicts before doing so.
+ return true;
+ }
+
bool supportsReadConcern(repl::ReadConcernLevel level) const override {
// Aggregations that are run directly against a collection allow any read concern.
// Otherwise, if the aggregate is collectionless then the read concern must be 'local'
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index 3bd009146ad..850e1823525 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -383,7 +383,8 @@ void _adjustChangeStreamReadConcern(OperationContext* opCtx) {
}
// Wait for read concern again since we changed the original read concern.
- uassertStatusOK(waitForReadConcern(opCtx, readConcernArgs, true, "aggregate"));
+ uassertStatusOK(
+ waitForReadConcern(opCtx, readConcernArgs, true, PrepareConflictBehavior::kIgnore));
}
} // namespace
diff --git a/src/mongo/db/read_concern.h b/src/mongo/db/read_concern.h
index 5f66a46f26c..8e8ca0b3575 100644
--- a/src/mongo/db/read_concern.h
+++ b/src/mongo/db/read_concern.h
@@ -43,6 +43,14 @@ class ReadConcernArgs;
class SpeculativeMajorityReadInfo;
}
+enum class PrepareConflictBehavior {
+ /* When prepare conflicts are encountered, block until the conflict is resolved. */
+ kEnforce,
+ /* Ignore prepare conflicts when they are encountered. This should only be enabled for
+ * operations than only perform reads. */
+ kIgnore
+};
+
/**
* Given the specified read concern arguments, performs checks that the read concern can actually be
* satisfied given the current state of the server and if so calls into the replication subsystem to
@@ -53,7 +61,7 @@ class SpeculativeMajorityReadInfo;
extern MONGO_DECLARE_SHIM((OperationContext * opCtx,
const repl::ReadConcernArgs& readConcernArgs,
bool allowAfterClusterTime,
- StringData cmdName)
+ PrepareConflictBehavior prepareConflictBehavior)
->Status) waitForReadConcern;
/*
diff --git a/src/mongo/db/read_concern_mongod.cpp b/src/mongo/db/read_concern_mongod.cpp
index 97a93de9788..7e1d0b101db 100644
--- a/src/mongo/db/read_concern_mongod.cpp
+++ b/src/mongo/db/read_concern_mongod.cpp
@@ -198,16 +198,10 @@ Status makeNoopWriteIfNeeded(OperationContext* opCtx, LogicalTime clusterTime) {
return Status::OK();
}
-// These commands are known to only perform reads, and therefore may be able to safely ignore
-// prepare conflicts. The exception is aggregate, which may do writes to an output collection, but
-// it enables enforcement of prepare conflicts before performing writes.
-static const stdx::unordered_set<std::string> ignorePrepareCommandWhitelist = {
- "aggregate", "count", "distinct", "find", "getMore", "group"};
-
/**
* Returns whether the command should ignore prepare conflicts or not.
*/
-bool shouldIgnorePrepared(StringData cmdName,
+bool shouldIgnorePrepared(PrepareConflictBehavior prepareConflictBehavior,
repl::ReadConcernLevel readConcernLevel,
boost::optional<LogicalTime> afterClusterTime,
boost::optional<LogicalTime> atClusterTime) {
@@ -223,11 +217,7 @@ bool shouldIgnorePrepared(StringData cmdName,
return false;
}
- if (ignorePrepareCommandWhitelist.count(cmdName.toString())) {
- return true;
- }
-
- return false;
+ return prepareConflictBehavior == PrepareConflictBehavior::kIgnore;
}
} // namespace
@@ -235,7 +225,7 @@ MONGO_REGISTER_SHIM(waitForReadConcern)
(OperationContext* opCtx,
const repl::ReadConcernArgs& readConcernArgs,
bool allowAfterClusterTime,
- StringData cmdName)
+ PrepareConflictBehavior prepareConflictBehavior)
->Status {
// If we are in a direct client within a transaction, then we may be holding locks, so it is
// illegal to wait for read concern. This is fine, since the outer operation should have handled
@@ -372,7 +362,7 @@ MONGO_REGISTER_SHIM(waitForReadConcern)
if (!opCtx->getClient()->isInDirectClient()) {
// Set whether this command should ignore prepare conflicts or not.
opCtx->recoveryUnit()->setIgnorePrepared(shouldIgnorePrepared(
- cmdName, readConcernArgs.getLevel(), afterClusterTime, atClusterTime));
+ prepareConflictBehavior, readConcernArgs.getLevel(), afterClusterTime, atClusterTime));
}
return Status::OK();
diff --git a/src/mongo/db/s/migration_source_manager.cpp b/src/mongo/db/s/migration_source_manager.cpp
index a4138943f9e..42e5a35a2d0 100644
--- a/src/mongo/db/s/migration_source_manager.cpp
+++ b/src/mongo/db/s/migration_source_manager.cpp
@@ -279,7 +279,8 @@ Status MigrationSourceManager::startClone(OperationContext* opCtx) {
auto const readConcernArgs = repl::ReadConcernArgs(
replCoord->getMyLastAppliedOpTime(), repl::ReadConcernLevel::kLocalReadConcern);
- uassertStatusOK(waitForReadConcern(opCtx, readConcernArgs, false, "moveChunk"));
+ uassertStatusOK(
+ waitForReadConcern(opCtx, readConcernArgs, false, PrepareConflictBehavior::kEnforce));
}
Status startCloneStatus = _cloneDriver->startClone(opCtx);
diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp
index 3d7a8097beb..533db3576e8 100644
--- a/src/mongo/db/service_entry_point_mongod.cpp
+++ b/src/mongo/db/service_entry_point_mongod.cpp
@@ -67,10 +67,14 @@ public:
void waitForReadConcern(OperationContext* opCtx,
const CommandInvocation* invocation,
const OpMsgRequest& request) const override {
+ const auto prepareConflictBehavior = invocation->canIgnorePrepareConflicts()
+ ? PrepareConflictBehavior::kIgnore
+ : PrepareConflictBehavior::kEnforce;
+
Status rcStatus = mongo::waitForReadConcern(opCtx,
repl::ReadConcernArgs::get(opCtx),
invocation->allowsAfterClusterTime(),
- request.getCommandName());
+ prepareConflictBehavior);
if (!rcStatus.isOK()) {
if (ErrorCodes::isExceededTimeLimitError(rcStatus.code())) {
diff --git a/src/mongo/embedded/read_concern_embedded.cpp b/src/mongo/embedded/read_concern_embedded.cpp
index baa683c7dfa..f8e367d0b74 100644
--- a/src/mongo/embedded/read_concern_embedded.cpp
+++ b/src/mongo/embedded/read_concern_embedded.cpp
@@ -37,7 +37,7 @@ MONGO_REGISTER_SHIM(waitForReadConcern)
(OperationContext* opCtx,
const repl::ReadConcernArgs& readConcernArgs,
bool allowAfterClusterTime,
- StringData cmdName)
+ PrepareConflictBehavior prepareConflictBehavior)
->Status {
if (readConcernArgs.getLevel() == repl::ReadConcernLevel::kLinearizableReadConcern) {
return {ErrorCodes::NotImplemented, "linearizable read concern not supported on embedded"};
diff --git a/src/mongo/embedded/service_entry_point_embedded.cpp b/src/mongo/embedded/service_entry_point_embedded.cpp
index 6a7c17e5b64..8c19c925ea5 100644
--- a/src/mongo/embedded/service_entry_point_embedded.cpp
+++ b/src/mongo/embedded/service_entry_point_embedded.cpp
@@ -50,10 +50,13 @@ public:
void waitForReadConcern(OperationContext* opCtx,
const CommandInvocation* invocation,
const OpMsgRequest& request) const override {
+ const auto prepareConflictBehavior = invocation->canIgnorePrepareConflicts()
+ ? PrepareConflictBehavior::kIgnore
+ : PrepareConflictBehavior::kEnforce;
auto rcStatus = mongo::waitForReadConcern(opCtx,
repl::ReadConcernArgs::get(opCtx),
invocation->allowsAfterClusterTime(),
- request.getCommandName());
+ prepareConflictBehavior);
uassertStatusOK(rcStatus);
}