summaryrefslogtreecommitdiff
path: root/src/mongo/db/read_concern_mongod.cpp
diff options
context:
space:
mode:
authorLouis Williams <louis.williams@mongodb.com>2019-03-14 12:34:23 -0400
committerLouis Williams <louis.williams@mongodb.com>2019-03-19 17:03:40 -0400
commit197233a97c2a8859b82ba1ffeac97ba2719f6470 (patch)
tree56d7bfb37a6957c27bc7ee13c062086742a59fa9 /src/mongo/db/read_concern_mongod.cpp
parent81d045a17ddf08e9f0eb7f30f96b45a352fab5cc (diff)
downloadmongo-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.cpp50
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();