diff options
author | Louis Williams <louis.williams@mongodb.com> | 2019-03-14 12:34:23 -0400 |
---|---|---|
committer | Louis Williams <louis.williams@mongodb.com> | 2019-03-19 17:03:40 -0400 |
commit | 197233a97c2a8859b82ba1ffeac97ba2719f6470 (patch) | |
tree | 56d7bfb37a6957c27bc7ee13c062086742a59fa9 /src/mongo/db/read_concern_mongod.cpp | |
parent | 81d045a17ddf08e9f0eb7f30f96b45a352fab5cc (diff) | |
download | mongo-197233a97c2a8859b82ba1ffeac97ba2719f6470.tar.gz |
SERVER-39074 All operations enforce prepare conflicts by default
Prepare conflicts may only be safely ignored when a command can
guarantee it does not perform writes. Prepare conflicts are ignored when
the read concern is local, available, or majority and the command is
aggregate, count, distinct, find, getMore, or group. Aggregate is a
special case because it may perform writes to an output collection, but
it enables prepare conflict enforcement before doing so.
Additionally, connections from a DBDirectClient inherit the
ignore_prepare state from their parent operation.
Diffstat (limited to 'src/mongo/db/read_concern_mongod.cpp')
-rw-r--r-- | src/mongo/db/read_concern_mongod.cpp | 50 |
1 files changed, 41 insertions, 9 deletions
diff --git a/src/mongo/db/read_concern_mongod.cpp b/src/mongo/db/read_concern_mongod.cpp index c961956ec48..97a93de9788 100644 --- a/src/mongo/db/read_concern_mongod.cpp +++ b/src/mongo/db/read_concern_mongod.cpp @@ -197,10 +197,45 @@ 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, + repl::ReadConcernLevel readConcernLevel, + boost::optional<LogicalTime> afterClusterTime, + boost::optional<LogicalTime> atClusterTime) { + + // Only these read concern levels are eligible for ignoring prepare conflicts. + if (readConcernLevel != repl::ReadConcernLevel::kLocalReadConcern && + readConcernLevel != repl::ReadConcernLevel::kAvailableReadConcern && + readConcernLevel != repl::ReadConcernLevel::kMajorityReadConcern) { + return false; + } + + if (afterClusterTime || atClusterTime) { + return false; + } + + if (ignorePrepareCommandWhitelist.count(cmdName.toString())) { + return true; + } + + return false; +} } // namespace MONGO_REGISTER_SHIM(waitForReadConcern) -(OperationContext* opCtx, const repl::ReadConcernArgs& readConcernArgs, bool allowAfterClusterTime) +(OperationContext* opCtx, + const repl::ReadConcernArgs& readConcernArgs, + bool allowAfterClusterTime, + StringData cmdName) ->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 @@ -208,7 +243,6 @@ MONGO_REGISTER_SHIM(waitForReadConcern) // should block on prepared transactions. if (opCtx->getClient()->isInDirectClient() && readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern) { - opCtx->recoveryUnit()->setIgnorePrepared(false); return Status::OK(); } @@ -334,13 +368,11 @@ MONGO_REGISTER_SHIM(waitForReadConcern) << " with readTs: " << opCtx->recoveryUnit()->getPointInTimeReadTimestamp(); } - // Only snapshot, linearizable and afterClusterTime reads should block on prepared transactions. - if (readConcernArgs.getLevel() != repl::ReadConcernLevel::kSnapshotReadConcern && - readConcernArgs.getLevel() != repl::ReadConcernLevel::kLinearizableReadConcern && - !afterClusterTime && !atClusterTime) { - opCtx->recoveryUnit()->setIgnorePrepared(true); - } else { - opCtx->recoveryUnit()->setIgnorePrepared(false); + // DBDirectClient should inherit whether or not to ignore prepare conflicts from its parent. + if (!opCtx->getClient()->isInDirectClient()) { + // Set whether this command should ignore prepare conflicts or not. + opCtx->recoveryUnit()->setIgnorePrepared(shouldIgnorePrepared( + cmdName, readConcernArgs.getLevel(), afterClusterTime, atClusterTime)); } return Status::OK(); |