summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/core/currentop_cursors.js31
-rw-r--r--src/mongo/db/clientcursor.cpp5
-rw-r--r--src/mongo/db/clientcursor.h11
-rw-r--r--src/mongo/db/generic_cursor.idl4
-rw-r--r--src/mongo/db/pipeline/mongo_process_common.cpp20
5 files changed, 66 insertions, 5 deletions
diff --git a/jstests/core/currentop_cursors.js b/jstests/core/currentop_cursors.js
index be8afeca54c..f46d7c82f84 100644
--- a/jstests/core/currentop_cursors.js
+++ b/jstests/core/currentop_cursors.js
@@ -8,6 +8,9 @@
(function() {
"use strict";
const coll = db.jstests_currentop_cursors;
+
+ load("jstests/libs/fixture_helpers.js"); // for FixtureHelpers
+
// Avoiding using the shell helper to avoid the implicit collection recreation.
db.runCommand({drop: coll.getName()});
assert.commandWorked(db.createCollection(coll.getName(), {capped: true, size: 1000}));
@@ -63,6 +66,12 @@
},
assertFunc: function(cursorId, result) {
assert.eq(result.length, 1, result);
+ // Plan summary does not exist on mongos, so skip this test on mongos.
+ if (!FixtureHelpers.isMongos(db)) {
+ assert.eq(result[0].planSummary, "COLLSCAN", result);
+ } else {
+ assert(!result[0].hasOwnProperty("planSummary"), result);
+ }
const idleCursor = result[0].cursor;
assert.eq(idleCursor.nDocsReturned, 2, result);
assert.eq(idleCursor.nBatchesReturned, 1, result);
@@ -71,6 +80,7 @@
assert.eq(idleCursor.noCursorTimeout, false, result);
assert.eq(idleCursor.originatingCommand.batchSize, 2, result);
assert.lte(idleCursor.createdDate, idleCursor.lastAccessDate, result);
+ assert(!idleCursor.hasOwnProperty("planSummary"), result);
}
});
runTest({
@@ -189,4 +199,25 @@
}
});
+ // planSummary does not exist on Mongos, so skip this test.
+ if (!FixtureHelpers.isMongos(db)) {
+ runTest({
+ findFunc: function() {
+ assert.commandWorked(coll.createIndex({"val": 1}));
+ return assert
+ .commandWorked(db.runCommand({
+ find: "jstests_currentop_cursors",
+ filter: {"val": {$gt: 2}},
+ batchSize: 2
+ }))
+ .cursor.id;
+
+ },
+ assertFunc: function(cursorId, result) {
+ assert.eq(result.length, 1, result);
+ assert.eq(result[0].planSummary, "IXSCAN { val: 1 }", result);
+
+ }
+ });
+ }
})();
diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp
index 756f1fbc7eb..92e6a1ffb30 100644
--- a/src/mongo/db/clientcursor.cpp
+++ b/src/mongo/db/clientcursor.cpp
@@ -47,6 +47,7 @@
#include "mongo/db/commands/server_status_metric.h"
#include "mongo/db/cursor_server_params.h"
#include "mongo/db/jsobj.h"
+#include "mongo/db/query/explain.h"
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/util/background.h"
@@ -92,7 +93,8 @@ ClientCursor::ClientCursor(ClientCursorParams params,
_exec(std::move(params.exec)),
_operationUsingCursor(operationUsingCursor),
_lastUseDate(now),
- _createdDate(now) {
+ _createdDate(now),
+ _planSummary(Explain::getPlanSummary(_exec.get())) {
invariant(_cursorManager);
invariant(_exec);
invariant(_operationUsingCursor);
@@ -143,6 +145,7 @@ GenericCursor ClientCursor::toGenericCursor() const {
gc.setLastAccessDate(getLastUseDate());
gc.setCreatedDate(getCreatedDate());
gc.setNBatchesReturned(getNBatches());
+ gc.setPlanSummary(getPlanSummary());
if (auto opCtx = _operationUsingCursor) {
gc.setOperationUsingCursorId(opCtx->getOpID());
}
diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h
index 96cae17da5b..83b326b93da 100644
--- a/src/mongo/db/clientcursor.h
+++ b/src/mongo/db/clientcursor.h
@@ -209,6 +209,14 @@ public:
return _createdDate;
}
+ void setPlanSummary(std::string ps) {
+ _planSummary = std::move(ps);
+ }
+
+ StringData getPlanSummary() const {
+ return StringData(_planSummary);
+ }
+
/**
* Returns a generic cursor containing diagnostics about this cursor.
* The caller must either have this cursor pinned or hold a mutex from the cursor manager.
@@ -374,6 +382,9 @@ private:
Date_t _lastUseDate;
Date_t _createdDate;
+
+ // A string with the plan summary of the cursor's query.
+ std::string _planSummary;
};
/**
diff --git a/src/mongo/db/generic_cursor.idl b/src/mongo/db/generic_cursor.idl
index 24661fa9221..2792a9db88e 100644
--- a/src/mongo/db/generic_cursor.idl
+++ b/src/mongo/db/generic_cursor.idl
@@ -71,6 +71,10 @@ structs:
lsid:
type: LogicalSessionId
optional: true
+ planSummary:
+ description: The plan summary of this cursor's query.
+ type: string
+ optional: true
operationUsingCursorId:
description: The op ID of the operation pinning the cursor. Will be empty for idle cursors.
type: long
diff --git a/src/mongo/db/pipeline/mongo_process_common.cpp b/src/mongo/db/pipeline/mongo_process_common.cpp
index aae991ba05f..1c97921e237 100644
--- a/src/mongo/db/pipeline/mongo_process_common.cpp
+++ b/src/mongo/db/pipeline/mongo_process_common.cpp
@@ -78,10 +78,22 @@ std::vector<BSONObj> MongoProcessCommon::getCurrentOps(
if (cursorMode == CurrentOpCursorMode::kIncludeCursors) {
for (auto&& cursor : getIdleCursors(expCtx, userMode)) {
- ops.push_back(BSON("type"
- << "idleCursor"
- << "cursor"
- << cursor.toBSON()));
+ BSONObjBuilder cursorObj;
+ cursorObj.append("type", "idleCursor");
+ // On mongos, planSummary is not present.
+ auto planSummaryData = cursor.getPlanSummary();
+ if (planSummaryData) {
+ auto planSummaryText = planSummaryData->toString();
+ // Plan summary has to appear in the top level object, not the cursor object.
+ // We remove it, create the op, then put it back.
+ cursor.setPlanSummary(boost::none);
+ cursorObj.append("planSummary", planSummaryText);
+ cursorObj.append("cursor", cursor.toBSON());
+ cursor.setPlanSummary(StringData(planSummaryText));
+ } else {
+ cursorObj.append("cursor", cursor.toBSON());
+ }
+ ops.emplace_back(cursorObj.obj());
}
}