diff options
-rw-r--r-- | jstests/noPassthrough/readConcern_snapshot.js | 24 | ||||
-rw-r--r-- | jstests/noPassthrough/read_concern_snapshot_yielding.js | 69 | ||||
-rw-r--r-- | jstests/noPassthrough/snapshot_reads.js | 79 | ||||
-rw-r--r-- | src/mongo/db/commands/count_cmd.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/commands/distinct.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/commands/group_cmd.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/commands/parallel_collection_scan.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/query/get_executor.cpp | 32 | ||||
-rw-r--r-- | src/mongo/db/query/get_executor.h | 14 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 12 |
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); } } |