summaryrefslogtreecommitdiff
path: root/jstests/core/administrative/current_op/currentop_cursors.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/administrative/current_op/currentop_cursors.js')
-rw-r--r--jstests/core/administrative/current_op/currentop_cursors.js248
1 files changed, 248 insertions, 0 deletions
diff --git a/jstests/core/administrative/current_op/currentop_cursors.js b/jstests/core/administrative/current_op/currentop_cursors.js
new file mode 100644
index 00000000000..2d3608b21ab
--- /dev/null
+++ b/jstests/core/administrative/current_op/currentop_cursors.js
@@ -0,0 +1,248 @@
+/**
+ * Tests that an idle cursor will appear in the $currentOp output if the idleCursors option is
+ * set to true.
+ *
+ * @tags: [
+ * assumes_read_concern_unchanged,
+ * assumes_read_preference_unchanged,
+ * requires_capped,
+ * no_selinux,
+ * # This test contains assertions for the hostname that operations run on.
+ * tenant_migration_incompatible,
+ * docker_incompatible,
+ * # TODO SERVER-70142: Populate planSummary field which is shown in output of $currentOp.
+ * cqf_incompatible,
+ * ]
+ */
+
+(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}));
+for (let i = 0; i < 30; ++i) {
+ assert.commandWorked(coll.insert({"val": i}));
+}
+/**
+ * runTest creates a new collection called jstests_currentop_cursors and then runs the provided
+ * find query. It calls $currentOp and does some basic assertions to make sure idleCursors is
+ * behaving as intended in each case.
+ * findFunc: A function that runs a find query. Is expected to return a cursorID.
+ * Arbitrary code can be run in findFunc as long as it returns a cursorID.
+ * assertFunc: A function that runs assertions against the results of the $currentOp.
+ * Takes the following arguments
+ * 'findOut': The cursorID returned from findFunc.
+ * 'result': The results from running $currenpOp as an array of JSON objects.
+ * Arbitrary code can be run in assertFunc, and there is no return value needed.
+ */
+function runTest({findFunc, assertFunc}) {
+ const adminDB = db.getSiblingDB("admin");
+ const findOut = findFunc();
+ const result = adminDB
+ .aggregate([
+ {$currentOp: {localOps: true, allUsers: false, idleCursors: true}},
+ {$match: {$and: [{type: "idleCursor"}, {"cursor.cursorId": findOut}]}}
+ ])
+ .toArray();
+ assert.eq(result[0].ns, coll.getFullName(), result);
+ assert.eq(result[0].cursor.originatingCommand.find, coll.getName(), result);
+ assertFunc(findOut, result);
+ const noIdle = adminDB
+ .aggregate([
+ {$currentOp: {allUsers: false, idleCursors: false}},
+ {$match: {$and: [{type: "idleCursor"}, {"cursor.cursorId": findOut}]}}
+ ])
+ .toArray();
+ assert.eq(noIdle.length, 0, tojson(noIdle));
+ const noFlag =
+ adminDB.aggregate([{$currentOp: {allUsers: false}}, {$match: {type: "idleCursor"}}])
+ .toArray();
+
+ assert.eq(noIdle.length, 0, tojson(noFlag));
+}
+
+// Basic test with default values.
+runTest({
+ findFunc: function() {
+ return assert
+ .commandWorked(db.runCommand({find: "jstests_currentop_cursors", batchSize: 2}))
+ .cursor.id;
+ },
+ 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);
+ }
+ assert(result[0].lsid.hasOwnProperty('id'), result);
+ assert(result[0].lsid.hasOwnProperty('uid'), result);
+ const uri = new MongoURI(db.getMongo().host);
+ assert(uri.servers.some((server) => {
+ return result[0].host == getHostName() + ":" + server.port;
+ }));
+ const idleCursor = result[0].cursor;
+ assert.eq(idleCursor.nDocsReturned, 2, result);
+ assert.eq(idleCursor.nBatchesReturned, 1, result);
+ assert.eq(idleCursor.tailable, false, result);
+ assert.eq(idleCursor.awaitData, false, result);
+ assert.eq(idleCursor.noCursorTimeout, false, result);
+ assert.eq(idleCursor.originatingCommand.batchSize, 2, result);
+ assert.lte(idleCursor.createdDate, idleCursor.lastAccessDate, result);
+ // Make sure that the top level fields do not also appear in the cursor subobject.
+ assert(!idleCursor.hasOwnProperty("planSummary"), result);
+ assert(!idleCursor.hasOwnProperty('host'), result);
+ assert(!idleCursor.hasOwnProperty('lsid'), result);
+ }
+});
+
+// Test that tailable, awaitData, and noCursorTimeout are set.
+runTest({
+ findFunc: function() {
+ return assert
+ .commandWorked(db.runCommand({
+ find: "jstests_currentop_cursors",
+ batchSize: 2,
+ tailable: true,
+ awaitData: true,
+ noCursorTimeout: true
+ }))
+ .cursor.id;
+ },
+ assertFunc: function(cursorId, result) {
+ assert.eq(result.length, 1, result);
+ const idleCursor = result[0].cursor;
+ assert.eq(idleCursor.tailable, true, result);
+ assert.eq(idleCursor.awaitData, true, result);
+ assert.eq(idleCursor.noCursorTimeout, true, result);
+ assert.eq(idleCursor.originatingCommand.batchSize, 2, result);
+ }
+});
+
+// Test that dates are set correctly.
+runTest({
+ findFunc: function() {
+ return assert
+ .commandWorked(db.runCommand({find: "jstests_currentop_cursors", batchSize: 2}))
+ .cursor.id;
+ },
+ assertFunc: function(cursorId, result) {
+ const adminDB = db.getSiblingDB("admin");
+ // Make sure the two cursors have different creation times.
+ assert.soon(() => {
+ const secondCursor = assert.commandWorked(
+ db.runCommand({find: "jstests_currentop_cursors", batchSize: 2}));
+
+ const secondResult =
+ adminDB
+ .aggregate([
+ {$currentOp: {localOps: true, allUsers: false, idleCursors: true}},
+ {
+ $match: {
+ $and: [
+ {type: "idleCursor"},
+ {"cursor.cursorId": secondCursor.cursor.id}
+ ]
+ }
+ }
+ ])
+ .toArray();
+ return result[0].cursor.createdDate < secondResult[0].cursor.createdDate;
+ });
+ }
+});
+
+// Test larger batch size.
+runTest({
+ findFunc: function() {
+ return assert
+ .commandWorked(db.runCommand(
+ {find: "jstests_currentop_cursors", batchSize: 4, noCursorTimeout: true}))
+ .cursor.id;
+ },
+ assertFunc: function(cursorId, result) {
+ const idleCursor = result[0].cursor;
+ assert.eq(result.length, 1, result);
+ assert.eq(idleCursor.nDocsReturned, 4, result);
+ assert.eq(idleCursor.nBatchesReturned, 1, result);
+ assert.eq(idleCursor.noCursorTimeout, true, result);
+ assert.eq(idleCursor.originatingCommand.batchSize, 4, result);
+ }
+});
+
+// Test batchSize and nDocs are incremented correctly.
+runTest({
+ findFunc: function() {
+ return assert
+ .commandWorked(db.runCommand({find: "jstests_currentop_cursors", batchSize: 2}))
+ .cursor.id;
+ },
+ assertFunc: function(cursorId, result) {
+ const adminDB = db.getSiblingDB("admin");
+ const originalAccess = result[0].cursor.lastAccessDate;
+ assert.commandWorked(db.runCommand(
+ {getMore: cursorId, collection: "jstests_currentop_cursors", batchSize: 2}));
+ result = adminDB
+ .aggregate([
+ {$currentOp: {localOps: true, allUsers: false, idleCursors: true}},
+ {$match: {$and: [{type: "idleCursor"}, {"cursor.cursorId": cursorId}]}}
+ ])
+ .toArray();
+ let idleCursor = result[0].cursor;
+ assert.eq(idleCursor.nDocsReturned, 4, result);
+ assert.eq(idleCursor.nBatchesReturned, 2, result);
+ assert.eq(idleCursor.originatingCommand.batchSize, 2, result);
+ // Make sure that the getMore will not finish running in the same milli as the cursor
+ // creation.
+ assert.soon(() => {
+ assert.commandWorked(db.runCommand(
+ {getMore: cursorId, collection: "jstests_currentop_cursors", batchSize: 2}));
+ result = adminDB
+ .aggregate([
+ {$currentOp: {localOps: true, allUsers: false, idleCursors: true}},
+ {$match: {$and: [{type: "idleCursor"}, {"cursor.cursorId": cursorId}]}}
+ ])
+ .toArray();
+ idleCursor = result[0].cursor;
+ return idleCursor.createdDate < idleCursor.lastAccessDate &&
+ originalAccess < idleCursor.lastAccessDate;
+ });
+ }
+});
+
+// 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);
+ }
+ });
+}
+// Test lsid.id value is correct.
+const session = db.getMongo().startSession();
+runTest({
+ findFunc: function() {
+ const sessionDB = session.getDatabase("test");
+ return assert
+ .commandWorked(sessionDB.runCommand({find: "jstests_currentop_cursors", batchSize: 2}))
+ .cursor.id;
+ },
+ assertFunc: function(cursorId, result) {
+ assert.eq(result.length, 1, result);
+ assert.eq(session.getSessionId().id, result[0].lsid.id);
+ }
+});
+})();