diff options
-rw-r--r-- | src/mongo/db/commands.h | 16 | ||||
-rw-r--r-- | src/mongo/db/commands/count_cmd.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/distinct.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/find_cmd.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/getmore_cmd.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/pipeline_command.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/commands/run_aggregate.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/read_concern.h | 10 | ||||
-rw-r--r-- | src/mongo/db/read_concern_mongod.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/s/migration_source_manager.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_mongod.cpp | 6 | ||||
-rw-r--r-- | src/mongo/embedded/read_concern_embedded.cpp | 2 | ||||
-rw-r--r-- | src/mongo/embedded/service_entry_point_embedded.cpp | 5 |
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); } |