summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/noPassthrough/readConcern_snapshot.js24
-rw-r--r--jstests/noPassthrough/read_concern_snapshot_yielding.js69
-rw-r--r--jstests/noPassthrough/snapshot_reads.js79
-rw-r--r--src/mongo/db/commands/count_cmd.cpp14
-rw-r--r--src/mongo/db/commands/distinct.cpp7
-rw-r--r--src/mongo/db/commands/group_cmd.cpp6
-rw-r--r--src/mongo/db/commands/parallel_collection_scan.cpp10
-rw-r--r--src/mongo/db/query/get_executor.cpp32
-rw-r--r--src/mongo/db/query/get_executor.h14
-rw-r--r--src/mongo/db/service_entry_point_common.cpp12
10 files changed, 189 insertions, 78 deletions
diff --git a/jstests/noPassthrough/readConcern_snapshot.js b/jstests/noPassthrough/readConcern_snapshot.js
index e7ff6b63b40..bf9ff913f8f 100644
--- a/jstests/noPassthrough/readConcern_snapshot.js
+++ b/jstests/noPassthrough/readConcern_snapshot.js
@@ -122,15 +122,13 @@
assert.commandWorked(sessionDb.runCommand(
{count: collName, readConcern: {level: "snapshot"}, txnNumber: NumberLong(txnNumber++)}));
- // readConcern 'snapshot' is not supported by distinct.
- // TODO SERVER-33354: Add snapshot support for distinct.
- assert.commandFailedWithCode(sessionDb.runCommand({
+ // readConcern 'snapshot' is supported by distinct.
+ assert.commandWorked(sessionDb.runCommand({
distinct: collName,
key: "x",
readConcern: {level: "snapshot"},
txnNumber: NumberLong(txnNumber++)
- }),
- ErrorCodes.InvalidOptions);
+ }));
// readConcern 'snapshot' is not supported by geoNear.
// TODO SERVER-33354: Add snapshot support for geoNear.
@@ -153,13 +151,11 @@
}));
// readConcern 'snapshot' is supported by group.
- // TODO SERVER-33354: Add snapshot support for group.
- assert.commandFailedWithCode(sessionDb.runCommand({
+ assert.commandWorked(sessionDb.runCommand({
group: {ns: collName, key: {_id: 1}, $reduce: function(curr, result) {}, initial: {}},
readConcern: {level: "snapshot"},
txnNumber: NumberLong(txnNumber++)
- }),
- ErrorCodes.InvalidOptions);
+ }));
// TODO SERVER-33412 Move all write related commands out of this test file when writes
// with snapshot read concern are only allowed in transactions.
@@ -235,12 +231,18 @@
assert.eq(0, sessionDb.coll.find({_id: 1}).itcount());
// readConcern 'snapshot' is supported by parallelCollectionScan.
- assert.commandWorked(sessionDb.runCommand({
+ const res = assert.commandWorked(sessionDb.runCommand({
parallelCollectionScan: collName,
numCursors: 1,
readConcern: {level: "snapshot"},
- txnNumber: NumberLong(txnNumber++)
+ txnNumber: NumberLong(txnNumber)
}));
+ assert(res.hasOwnProperty("cursors"));
+ assert.eq(res.cursors.length, 1);
+ assert(res.cursors[0].hasOwnProperty("cursor"));
+ const cursorId = res.cursors[0].cursor.id;
+ assert.commandWorked(sessionDb.runCommand(
+ {getMore: cursorId, collection: collName, txnNumber: NumberLong(txnNumber++)}));
// readConcern 'snapshot' is not supported by non-CRUD commands.
assert.commandFailedWithCode(sessionDb.runCommand({
diff --git a/jstests/noPassthrough/read_concern_snapshot_yielding.js b/jstests/noPassthrough/read_concern_snapshot_yielding.js
index 777e8ca663c..9fb3e51c378 100644
--- a/jstests/noPassthrough/read_concern_snapshot_yielding.js
+++ b/jstests/noPassthrough/read_concern_snapshot_yielding.js
@@ -209,7 +209,7 @@
assert.eq(res.cursor.firstBatch.length, TestData.numDocs, tojson(res));
}, {"command.filter": {x: 1}}, {op: "query"});
- // Test getMore.
+ // Test getMore on a find established cursor.
testCommand(function() {
assert.commandWorked(db.adminCommand(
{configureFailPoint: "setInterruptOnlyPlansCheckForInterruptHang", mode: "off"}));
@@ -278,6 +278,73 @@
res.cursor.nextBatch.length, TestData.numDocs - initialFindBatchSize, tojson(res));
}, {"originatingCommand.filter": {x: 1}}, {op: "getmore"});
+ // Test count.
+ testCommand(function() {
+ const res = assert.commandWorked(db.runCommand({
+ count: "coll",
+ query: {_id: {$ne: 0}},
+ readConcern: {level: "snapshot"},
+ lsid: TestData.sessionId,
+ txnNumber: NumberLong(TestData.txnNumber)
+ }));
+ assert.eq(res.n, 3, tojson(res));
+ }, {"command.count": "coll"}, {"command.count": "coll"});
+
+ // Test distinct.
+ testCommand(function() {
+ const res = assert.commandWorked(db.runCommand({
+ distinct: "coll",
+ key: "_id",
+ readConcern: {level: "snapshot"},
+ lsid: TestData.sessionId,
+ txnNumber: NumberLong(TestData.txnNumber)
+ }));
+ assert(res.hasOwnProperty("values"));
+ assert.eq(res.values.length, 4, tojson(res));
+ }, {"command.distinct": "coll"}, {"command.distinct": "coll"});
+
+ // Test group.
+ testCommand(function() {
+ const res = assert.commandWorked(db.runCommand({
+ group: {ns: "coll", key: {_id: 1}, $reduce: function(curr, result) {}, initial: {}},
+ readConcern: {level: "snapshot"},
+ lsid: TestData.sessionId,
+ txnNumber: NumberLong(TestData.txnNumber)
+ }));
+ assert(res.hasOwnProperty("count"), tojson(res));
+ assert.eq(res.count, 4);
+ }, {"command.group.ns": "coll"}, {"command.group.ns": "coll"});
+
+ // Test getMore on a parallelCollectionScan established cursor. We skip testing for
+ // parallelCollectionScan itself as it returns a cursor only and may not hit an interrupt point.
+ testCommand(function() {
+ assert.commandWorked(db.adminCommand(
+ {configureFailPoint: "setInterruptOnlyPlansCheckForInterruptHang", mode: "off"}));
+ let res = assert.commandWorked(db.runCommand({
+ parallelCollectionScan: "coll",
+ numCursors: 1,
+ readConcern: {level: "snapshot"},
+ lsid: TestData.sessionId,
+ txnNumber: NumberLong(TestData.txnNumber)
+ }));
+ assert(res.hasOwnProperty("cursors"));
+ assert.eq(res.cursors.length, 1, tojson(res));
+ assert(res.cursors[0].hasOwnProperty("cursor"), tojson(res));
+ const cursorId = res.cursors[0].cursor.id;
+
+ assert.commandWorked(db.adminCommand(
+ {configureFailPoint: "setInterruptOnlyPlansCheckForInterruptHang", mode: "alwaysOn"}));
+ res = assert.commandWorked(db.runCommand({
+ getMore: NumberLong(cursorId),
+ collection: "coll",
+ lsid: TestData.sessionId,
+ txnNumber: NumberLong(TestData.txnNumber)
+ }));
+ assert(res.hasOwnProperty("cursor"), tojson(res));
+ assert(res.cursor.hasOwnProperty("nextBatch"), tojson(res));
+ assert.eq(res.cursor.nextBatch.length, TestData.numDocs, tojson(res));
+ }, {"originatingCommand.parallelCollectionScan": "coll"}, {op: "getmore"});
+
// Test update.
// TODO SERVER-33412: Perform writes under autocommit:false transaction.
// TODO SERVER-33548: We cannot provide a 'profilerFilter' because profiling is turned off for
diff --git a/jstests/noPassthrough/snapshot_reads.js b/jstests/noPassthrough/snapshot_reads.js
index 5f6f7774d98..a431861aab4 100644
--- a/jstests/noPassthrough/snapshot_reads.js
+++ b/jstests/noPassthrough/snapshot_reads.js
@@ -20,6 +20,20 @@
}
const secondaryDB = rst.getSecondary().getDB(dbName);
+ function parseCursor(cmdResult) {
+ if (cmdResult.hasOwnProperty("cursor")) {
+ assert(cmdResult.cursor.hasOwnProperty("id"));
+ return cmdResult.cursor;
+ } else if (cmdResult.hasOwnProperty("cursors") && cmdResult.cursors.length === 1 &&
+ cmdResult.cursors[0].hasOwnProperty("cursor")) {
+ assert(cmdResult.cursors[0].cursor.hasOwnProperty("id"));
+ return cmdResult.cursors[0].cursor;
+ }
+
+ throw Error("parseCursor failed to find cursor object. Command Result: " +
+ tojson(cmdResult));
+ }
+
function runTest({useCausalConsistency, readFromSecondary, establishCursorCmd}) {
primaryDB.coll.drop();
@@ -51,14 +65,11 @@
// Establish a snapshot batchSize:0 cursor.
let res = assert.commandWorked(sessionDb.runCommand(cursorCmd));
+ let cursor = parseCursor(res);
- assert(res.hasOwnProperty("cursor"), tojson(res));
- assert(res.cursor.hasOwnProperty("firstBatch"), tojson(res));
- assert.eq(0, res.cursor.firstBatch.length, tojson(res));
-
- assert(res.cursor.hasOwnProperty("id"), tojson(res));
- const cursorId = res.cursor.id;
- assert.neq(cursorId, 0);
+ assert(cursor.hasOwnProperty("firstBatch"), tojson(res));
+ assert.eq(0, cursor.firstBatch.length, tojson(res));
+ assert.neq(cursor.id, 0);
// Insert an 11th document which should not be visible to the snapshot cursor. This write is
// performed outside of the session.
@@ -66,35 +77,33 @@
// Fetch the first 5 documents.
res = assert.commandWorked(sessionDb.runCommand({
- getMore: cursorId,
+ getMore: cursor.id,
collection: collName,
batchSize: 5,
txnNumber: NumberLong(txnNumber)
}));
- assert(res.hasOwnProperty("cursor"), tojson(res));
- assert(res.cursor.hasOwnProperty("id"), tojson(res));
- assert.neq(0, res.cursor.id, tojson(res));
- assert(res.cursor.hasOwnProperty("nextBatch"), tojson(res));
- assert.eq(5, res.cursor.nextBatch.length, tojson(res));
+ cursor = parseCursor(res);
+ assert.neq(0, cursor.id, tojson(res));
+ assert(cursor.hasOwnProperty("nextBatch"), tojson(res));
+ assert.eq(5, cursor.nextBatch.length, tojson(res));
// Exhaust the cursor, retrieving the remainder of the result set. Performing a second
// getMore tests snapshot isolation across multiple getMore invocations.
res = assert.commandWorked(sessionDb.runCommand({
- getMore: cursorId,
+ getMore: cursor.id,
collection: collName,
batchSize: 20,
txnNumber: NumberLong(txnNumber++)
}));
// The cursor has been exhausted.
- assert(res.hasOwnProperty("cursor"), tojson(res));
- assert(res.cursor.hasOwnProperty("id"), tojson(res));
- assert.eq(0, res.cursor.id, tojson(res));
+ cursor = parseCursor(res);
+ assert.eq(0, cursor.id, tojson(res));
// Only the remaining 5 of the initial 10 documents are returned. The 11th document is not
// part of the result set.
- assert(res.cursor.hasOwnProperty("nextBatch"), tojson(res));
- assert.eq(5, res.cursor.nextBatch.length, tojson(res));
+ assert(cursor.hasOwnProperty("nextBatch"), tojson(res));
+ assert.eq(5, cursor.nextBatch.length, tojson(res));
if (readFromSecondary) {
rst.awaitLastOpCommitted();
@@ -110,13 +119,12 @@
}));
// The cursor has been exhausted.
- assert(res.hasOwnProperty("cursor"), tojson(res));
- assert(res.cursor.hasOwnProperty("id"), tojson(res));
- assert.eq(0, res.cursor.id, tojson(res));
+ cursor = parseCursor(res);
+ assert.eq(0, cursor.id, tojson(res));
// All 11 documents are returned.
- assert(res.cursor.hasOwnProperty("firstBatch"), tojson(res));
- assert.eq(11, res.cursor.firstBatch.length, tojson(res));
+ assert(cursor.hasOwnProperty("firstBatch"), tojson(res));
+ assert.eq(11, cursor.firstBatch.length, tojson(res));
// Reject snapshot reads without txnNumber.
assert.commandFailed(sessionDb.runCommand(
@@ -148,5 +156,28 @@
runTest({useCausalConsistency: false, readFromSecondary: true, establishCursorCmd: aggCmd});
runTest({useCausalConsistency: true, readFromSecondary: true, establishCursorCmd: aggCmd});
+ // Test snapshot reads using parallelCollectionScan.
+ let parallelCollScanCmd = {parallelCollectionScan: collName, numCursors: 1};
+ runTest({
+ useCausalConsistency: false,
+ readFromSecondary: false,
+ establishCursorCmd: parallelCollScanCmd
+ });
+ runTest({
+ useCausalConsistency: true,
+ readFromSecondary: false,
+ establishCursorCmd: parallelCollScanCmd
+ });
+ runTest({
+ useCausalConsistency: false,
+ readFromSecondary: true,
+ establishCursorCmd: parallelCollScanCmd
+ });
+ runTest({
+ useCausalConsistency: true,
+ readFromSecondary: true,
+ establishCursorCmd: parallelCollScanCmd
+ });
+
rst.stopSet();
})();
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index 85a6e1f02c8..91ba0169b0c 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -152,11 +152,8 @@ public:
// version on initial entry into count.
auto rangePreserver = CollectionShardingState::get(opCtx, nss)->getMetadata();
- auto statusWithPlanExecutor = getExecutorCount(opCtx,
- collection,
- request.getValue(),
- true, // explain
- PlanExecutor::YIELD_AUTO);
+ auto statusWithPlanExecutor =
+ getExecutorCount(opCtx, collection, request.getValue(), true /*explain*/);
if (!statusWithPlanExecutor.isOK()) {
return statusWithPlanExecutor.getStatus();
}
@@ -207,11 +204,8 @@ public:
// version on initial entry into count.
auto rangePreserver = CollectionShardingState::get(opCtx, nss)->getMetadata();
- auto statusWithPlanExecutor = getExecutorCount(opCtx,
- collection,
- request.getValue(),
- false, // !explain
- PlanExecutor::YIELD_AUTO);
+ auto statusWithPlanExecutor =
+ getExecutorCount(opCtx, collection, request.getValue(), false /*explain*/);
if (!statusWithPlanExecutor.isOK()) {
return CommandHelpers::appendCommandStatus(result, statusWithPlanExecutor.getStatus());
}
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index e97d9ee1933..e5cdeba3a60 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -148,8 +148,8 @@ public:
Collection* const collection = ctx->getCollection();
- auto executor = uassertStatusOK(getExecutorDistinct(
- opCtx, collection, nss.ns(), &parsedDistinct, PlanExecutor::YIELD_AUTO));
+ auto executor =
+ uassertStatusOK(getExecutorDistinct(opCtx, collection, nss.ns(), &parsedDistinct));
Explain::explainStages(executor.get(), collection, verbosity, out);
return Status::OK();
@@ -188,8 +188,7 @@ public:
Collection* const collection = ctx->getCollection();
- auto executor = getExecutorDistinct(
- opCtx, collection, nss.ns(), &parsedDistinct, PlanExecutor::YIELD_AUTO);
+ auto executor = getExecutorDistinct(opCtx, collection, nss.ns(), &parsedDistinct);
if (!executor.isOK()) {
return CommandHelpers::appendCommandStatus(result, executor.getStatus());
}
diff --git a/src/mongo/db/commands/group_cmd.cpp b/src/mongo/db/commands/group_cmd.cpp
index 3441e23a1ea..de3811cc6fa 100644
--- a/src/mongo/db/commands/group_cmd.cpp
+++ b/src/mongo/db/commands/group_cmd.cpp
@@ -134,8 +134,7 @@ private:
AutoGetCollectionForReadCommand ctx(opCtx, groupRequest.ns);
Collection* coll = ctx.getCollection();
- auto statusWithPlanExecutor =
- getExecutorGroup(opCtx, coll, groupRequest, PlanExecutor::YIELD_AUTO);
+ auto statusWithPlanExecutor = getExecutorGroup(opCtx, coll, groupRequest);
if (!statusWithPlanExecutor.isOK()) {
return statusWithPlanExecutor.getStatus();
}
@@ -164,8 +163,7 @@ private:
AutoGetCollectionForReadCommand ctx(opCtx, groupRequest.ns);
Collection* coll = ctx.getCollection();
- auto statusWithPlanExecutor =
- getExecutorGroup(opCtx, coll, groupRequest, PlanExecutor::YIELD_AUTO);
+ auto statusWithPlanExecutor = getExecutorGroup(opCtx, coll, groupRequest);
if (!statusWithPlanExecutor.isOK()) {
return CommandHelpers::appendCommandStatus(result, statusWithPlanExecutor.getStatus());
}
diff --git a/src/mongo/db/commands/parallel_collection_scan.cpp b/src/mongo/db/commands/parallel_collection_scan.cpp
index c040e9e0510..ae3818ab9ea 100644
--- a/src/mongo/db/commands/parallel_collection_scan.cpp
+++ b/src/mongo/db/commands/parallel_collection_scan.cpp
@@ -129,8 +129,15 @@ public:
make_unique<MultiIteratorStage>(opCtx, ws.get(), collection);
// Takes ownership of 'ws' and 'mis'.
+ const auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
auto statusWithPlanExecutor = PlanExecutor::make(
- opCtx, std::move(ws), std::move(mis), collection, PlanExecutor::YIELD_AUTO);
+ opCtx,
+ std::move(ws),
+ std::move(mis),
+ collection,
+ readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern
+ ? PlanExecutor::INTERRUPT_ONLY
+ : PlanExecutor::YIELD_AUTO);
invariant(statusWithPlanExecutor.isOK());
execs.push_back(std::move(statusWithPlanExecutor.getValue()));
}
@@ -167,6 +174,7 @@ public:
bucketsBuilder.append(threadResult.obj());
}
result.appendArray("cursors", bucketsBuilder.obj());
+ opCtx->setStashedCursor();
return true;
}
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index f69322c5329..ce5e2b3718e 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -1018,15 +1018,17 @@ StatusWith<unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorUpdate(
//
StatusWith<unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorGroup(
- OperationContext* opCtx,
- Collection* collection,
- const GroupRequest& request,
- PlanExecutor::YieldPolicy yieldPolicy) {
+ OperationContext* opCtx, Collection* collection, const GroupRequest& request) {
if (!getGlobalScriptEngine()) {
return Status(ErrorCodes::BadValue, "server-side JavaScript execution is disabled");
}
unique_ptr<WorkingSet> ws = make_unique<WorkingSet>();
+ const auto readConcernArgs = repl::ReadConcernArgs::get(opCtx);
+ const auto yieldPolicy =
+ readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern
+ ? PlanExecutor::INTERRUPT_ONLY
+ : PlanExecutor::YIELD_AUTO;
if (!collection) {
// Treat collections that do not exist as empty collections. Note that the explain
@@ -1269,11 +1271,7 @@ BSONObj getDistinctProjection(const std::string& field) {
} // namespace
StatusWith<unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorCount(
- OperationContext* opCtx,
- Collection* collection,
- const CountRequest& request,
- bool explain,
- PlanExecutor::YieldPolicy yieldPolicy) {
+ OperationContext* opCtx, Collection* collection, const CountRequest& request, bool explain) {
unique_ptr<WorkingSet> ws = make_unique<WorkingSet>();
auto qr = stdx::make_unique<QueryRequest>(request.getNs());
@@ -1296,6 +1294,13 @@ StatusWith<unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorCount(
return statusWithCQ.getStatus();
}
unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
+
+ const auto readConcernArgs = repl::ReadConcernArgs::get(opCtx);
+ const auto yieldPolicy =
+ readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern
+ ? PlanExecutor::INTERRUPT_ONLY
+ : PlanExecutor::YIELD_AUTO;
+
if (!collection) {
// Treat collections that do not exist as empty collections. Note that the explain
// reporting machinery always assumes that the root stage for a count operation is
@@ -1477,8 +1482,13 @@ StatusWith<unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorDistinct(
OperationContext* opCtx,
Collection* collection,
const std::string& ns,
- ParsedDistinct* parsedDistinct,
- PlanExecutor::YieldPolicy yieldPolicy) {
+ ParsedDistinct* parsedDistinct) {
+ const auto readConcernArgs = repl::ReadConcernArgs::get(opCtx);
+ const auto yieldPolicy =
+ readConcernArgs.getLevel() == repl::ReadConcernLevel::kSnapshotReadConcern
+ ? PlanExecutor::INTERRUPT_ONLY
+ : PlanExecutor::YIELD_AUTO;
+
if (!collection) {
// Treat collections that do not exist as empty collections.
return PlanExecutor::make(opCtx,
diff --git a/src/mongo/db/query/get_executor.h b/src/mongo/db/query/get_executor.h
index 6a801c1a81b..237f577de7d 100644
--- a/src/mongo/db/query/get_executor.h
+++ b/src/mongo/db/query/get_executor.h
@@ -122,8 +122,7 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorDist
OperationContext* opCtx,
Collection* collection,
const std::string& ns,
- ParsedDistinct* parsedDistinct,
- PlanExecutor::YieldPolicy yieldPolicy);
+ ParsedDistinct* parsedDistinct);
/*
* Get a PlanExecutor for a query executing as part of a count command.
@@ -133,11 +132,7 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorDist
* executing a count.
*/
StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorCount(
- OperationContext* opCtx,
- Collection* collection,
- const CountRequest& request,
- bool explain,
- PlanExecutor::YieldPolicy yieldPolicy);
+ OperationContext* opCtx, Collection* collection, const CountRequest& request, bool explain);
/**
* Get a PlanExecutor for a delete operation. 'parsedDelete' describes the query predicate
@@ -187,9 +182,6 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorUpda
* If an executor could not be created, returns a Status indicating why.
*/
StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorGroup(
- OperationContext* opCtx,
- Collection* collection,
- const GroupRequest& request,
- PlanExecutor::YieldPolicy yieldPolicy);
+ OperationContext* opCtx, Collection* collection, const GroupRequest& request);
} // namespace mongo
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 64f38e60008..b6b2ccbb8e3 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -132,10 +132,12 @@ const StringMap<int> sessionCheckoutWhitelist = {{"aggregate", 1},
const StringMap<int> readConcernSnapshotWhitelist = {{"aggregate", 1},
{"count", 1},
{"delete", 1},
+ {"distinct", 1},
{"find", 1},
{"findandmodify", 1},
{"findAndModify", 1},
{"geoSearch", 1},
+ {"group", 1},
{"insert", 1},
{"parallelCollectionScan", 1},
{"update", 1}};
@@ -1094,8 +1096,13 @@ DbResponse ServiceEntryPointCommon::handleRequest(OperationContext* opCtx,
DbMessage dbmsg(m);
Client& c = *opCtx->getClient();
+
if (c.isInDirectClient()) {
- invariant(!opCtx->lockState()->inAWriteUnitOfWork());
+ if (!opCtx->getLogicalSessionId() || !opCtx->getTxnNumber() ||
+ opCtx->recoveryUnit()->getReadConcernLevel() !=
+ repl::ReadConcernLevel::kSnapshotReadConcern) {
+ invariant(!opCtx->lockState()->inAWriteUnitOfWork());
+ }
} else {
LastError::get(c).startRequest();
AuthorizationSession::get(c)->startRequest(opCtx);
@@ -1202,6 +1209,8 @@ DbResponse ServiceEntryPointCommon::handleRequest(OperationContext* opCtx,
// Performance profiling is on
if (opCtx->lockState()->isReadLocked()) {
LOG(1) << "note: not profiling because recursive read lock";
+ } else if (c.isInDirectClient()) {
+ LOG(1) << "note: not profiling because we are in DBDirectClient";
} else if (behaviors.lockedForWriting()) {
// TODO SERVER-26825: Fix race condition where fsyncLock is acquired post
// lockedForWriting() call but prior to profile collection lock acquisition.
@@ -1209,6 +1218,7 @@ DbResponse ServiceEntryPointCommon::handleRequest(OperationContext* opCtx,
} else if (storageGlobalParams.readOnly) {
LOG(1) << "note: not profiling because server is read-only";
} else {
+ invariant(!opCtx->lockState()->inAWriteUnitOfWork());
profile(opCtx, op);
}
}