summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernard Gorman <bernard.gorman@gmail.com>2018-05-25 15:33:02 -0400
committerBernard Gorman <bernard.gorman@gmail.com>2019-03-24 12:33:12 +0000
commit941a6dc4a709ea3014786c3c5effe1f92a4acc23 (patch)
tree4dd85212883dac0e48b0c51723b010df761922b1
parentacad4974972d98a0582fbbe59e464b40ddc9c017 (diff)
downloadmongo-941a6dc4a709ea3014786c3c5effe1f92a4acc23.tar.gz
SERVER-34286 Ensure that operations never unexpectedly exceed the truncation threshold in currentop_query.js
(cherry picked from commit f0653f9963c2d2a597a4472aa065741f4ab17e57)
-rw-r--r--jstests/noPassthrough/currentop_query.js830
-rw-r--r--src/mongo/db/commands/SConscript5
-rw-r--r--src/mongo/db/commands/current_op_common.cpp23
3 files changed, 447 insertions, 411 deletions
diff --git a/jstests/noPassthrough/currentop_query.js b/jstests/noPassthrough/currentop_query.js
index b05e66d1af0..e7ea5883bb5 100644
--- a/jstests/noPassthrough/currentop_query.js
+++ b/jstests/noPassthrough/currentop_query.js
@@ -1,7 +1,7 @@
/**
* Confirms inclusion of query, command object and planSummary in currentOp() for CRUD operations.
* This test should not be run in the parallel suite as it sets fail points.
- * @tags: [requires_replication, requires_sharding, SERVER-34286]
+ * @tags: [requires_replication, requires_sharding]
*/
(function() {
"use strict";
@@ -59,8 +59,9 @@
* returns an array of matching current operations. This allows us to test output for both the
* currentOp command and the $currentOp aggregation stage.
* @params {boolean} truncatedOps - if true, we expect operations that exceed the maximum
- * currentOp size to be truncated in the output 'command' field. If false, we expect the entire
- * operation to be returned.
+ * currentOp size to be truncated in the output 'command' field, and we run only a subset of
+ * tests designed to exercise that scenario. If false, we expect the entire operation to be
+ * returned.
* @params {boolean} localOps - if true, we expect currentOp to return operations running on a
* mongoS itself rather than on the shards.
*/
@@ -76,6 +77,15 @@
const isLocalMongosCurOp = (FixtureHelpers.isMongos(testDB) && localOps);
const isRemoteShardCurOp = (FixtureHelpers.isMongos(testDB) && !localOps);
+ // If 'truncatedOps' is true, run only the subset of tests designed to validate the
+ // truncation behaviour. Otherwise, run the standard set of tests which assume that
+ // truncation will not occur.
+ if (truncatedOps) {
+ runTruncationTests();
+ } else {
+ runStandardTests();
+ }
+
/**
* Captures currentOp() for a given test command/operation and confirms that namespace,
* operation type and planSummary are correct.
@@ -131,7 +141,7 @@
// with the currentOp query argument.
assert.soon(
function() {
- var result = currentOp(testDB, testObj.currentOpFilter, localOps);
+ var result = currentOp(testDB, testObj.currentOpFilter, truncatedOps, localOps);
assert.commandWorked(result);
if (result.inprog.length > 0) {
@@ -148,9 +158,11 @@
},
function() {
return "Failed to find operation from " + tojson(testObj.currentOpFilter) +
- " in currentOp() output: " + tojson(currentOp(testDB, {}, localOps)) +
+ " in currentOp() output: " +
+ tojson(currentOp(testDB, {}, truncatedOps, localOps)) +
(isLocalMongosCurOp
- ? ", with localOps=false: " + tojson(currentOp(testDB, {}, false))
+ ? ", with localOps=false: " +
+ tojson(currentOp(testDB, {}, truncatedOps, false))
: "");
});
@@ -166,470 +178,478 @@
delete TestData.shellReadMode;
}
- //
- // Confirm currentOp content for commands defined in 'testList'.
- //
- var testList = [
- {
- test: function(db) {
- assert.eq(db.currentop_query
- .aggregate([{$match: {a: 1, $comment: "currentop_query"}}], {
- collation: {locale: "fr"},
- hint: {_id: 1},
- comment: "currentop_query_2"
- })
- .itcount(),
- 1);
- },
- planSummary: "IXSCAN { _id: 1 }",
- currentOpFilter: commandOrOriginatingCommand({
- "aggregate": {$exists: true},
- "pipeline.0.$match.$comment": "currentop_query",
- "comment": "currentop_query_2",
- "collation": {locale: "fr"},
- "hint": {_id: 1}
- },
- isRemoteShardCurOp)
- },
- {
- test: function(db) {
- assert.eq(db.currentop_query.find({a: 1, $comment: "currentop_query"})
- .collation({locale: "fr"})
- .count(),
- 1);
- },
- command: "count",
- planSummary: "COLLSCAN",
- currentOpFilter: {
- "command.query.$comment": "currentop_query",
- "command.collation": {locale: "fr"}
- }
- },
- {
- test: function(db) {
- assert.eq(
- db.currentop_query.distinct(
- "a", {a: 1, $comment: "currentop_query"}, {collation: {locale: "fr"}}),
- [1]);
- },
- command: "distinct",
- planSummary: "COLLSCAN",
- currentOpFilter: {
- "command.query.$comment": "currentop_query",
- "command.collation": {locale: "fr"}
- }
- },
- {
- test: function(db) {
- assert.eq(db.currentop_query.find({a: 1}).comment("currentop_query").itcount(),
- 1);
- },
- command: "find",
- planSummary: "COLLSCAN",
- currentOpFilter: {"command.comment": "currentop_query"}
- },
- {
- test: function(db) {
- assert.eq(db.currentop_query.findAndModify({
- query: {_id: 1, a: 1, $comment: "currentop_query"},
- update: {$inc: {b: 1}},
- collation: {locale: "fr"}
- }),
- {"_id": 1, "a": 1});
- },
- command: "findandmodify",
- planSummary: "IXSCAN { _id: 1 }",
- currentOpFilter: {
- "command.query.$comment": "currentop_query",
- "command.collation": {locale: "fr"}
- }
- },
- {
- test: function(db) {
- assert.commandWorked(
- db.currentop_query.mapReduce(() => {},
- (a, b) => {},
- {
- query: {$comment: "currentop_query"},
- out: {inline: 1},
- }));
- },
- command: "mapreduce",
- planSummary: "COLLSCAN",
- currentOpFilter: {
- "command.query.$comment": "currentop_query",
- "ns": /^currentop_query.*currentop_query/
- }
- },
- {
- test: function(db) {
- assert.writeOK(db.currentop_query.remove({a: 2, $comment: "currentop_query"},
- {collation: {locale: "fr"}}));
- },
- operation: "remove",
- planSummary: "COLLSCAN",
- currentOpFilter:
- (isLocalMongosCurOp ? {"command.delete": coll.getName(), "command.ordered": true}
- : {
- "command.q.$comment": "currentop_query",
- "command.collation": {locale: "fr"}
- })
- },
- {
- test: function(db) {
- assert.writeOK(
- db.currentop_query.update({a: 1, $comment: "currentop_query"},
- {$inc: {b: 1}},
- {collation: {locale: "fr"}, multi: true}));
- },
- operation: "update",
- planSummary: "COLLSCAN",
- currentOpFilter:
- (isLocalMongosCurOp ? {"command.update": coll.getName(), "command.ordered": true}
- : {
- "command.q.$comment": "currentop_query",
- "command.collation": {locale: "fr"}
- })
+ /**
+ * Runs a set of tests to verify that the currentOp output appears as expected. These tests
+ * assume that the 'truncateOps' parameter is false, so no command objects in the currentOp
+ * output will be truncated to string.
+ */
+ function runStandardTests() {
+ //
+ // Confirm currentOp content for commands defined in 'testList'.
+ //
+ var testList = [
+ {
+ test: function(db) {
+ assert.eq(db.currentop_query
+ .aggregate([{$match: {a: 1, $comment: "currentop_query"}}], {
+ collation: {locale: "fr"},
+ hint: {_id: 1},
+ comment: "currentop_query_2"
+ })
+ .itcount(),
+ 1);
+ },
+ planSummary: "IXSCAN { _id: 1 }",
+ currentOpFilter: commandOrOriginatingCommand({
+ "aggregate": {$exists: true},
+ "pipeline.0.$match.$comment": "currentop_query",
+ "comment": "currentop_query_2",
+ "collation": {locale: "fr"},
+ "hint": {_id: 1}
+ },
+ isRemoteShardCurOp)
+ },
+ {
+ test: function(db) {
+ assert.eq(db.currentop_query.find({a: 1, $comment: "currentop_query"})
+ .collation({locale: "fr"})
+ .count(),
+ 1);
+ },
+ command: "count",
+ planSummary: "COLLSCAN",
+ currentOpFilter: {
+ "command.query.$comment": "currentop_query",
+ "command.collation": {locale: "fr"}
+ }
+ },
+ {
+ test: function(db) {
+ assert.eq(db.currentop_query.distinct("a",
+ {a: 1, $comment: "currentop_query"},
+ {collation: {locale: "fr"}}),
+ [1]);
+ },
+ command: "distinct",
+ planSummary: "COLLSCAN",
+ currentOpFilter: {
+ "command.query.$comment": "currentop_query",
+ "command.collation": {locale: "fr"}
+ }
+ },
+ {
+ test: function(db) {
+ assert.eq(
+ db.currentop_query.find({a: 1}).comment("currentop_query").itcount(), 1);
+ },
+ command: "find",
+ planSummary: "COLLSCAN",
+ currentOpFilter: {"command.comment": "currentop_query"}
+ },
+ {
+ test: function(db) {
+ assert.eq(db.currentop_query.findAndModify({
+ query: {_id: 1, a: 1, $comment: "currentop_query"},
+ update: {$inc: {b: 1}},
+ collation: {locale: "fr"}
+ }),
+ {"_id": 1, "a": 1});
+ },
+ command: "findandmodify",
+ planSummary: "IXSCAN { _id: 1 }",
+ currentOpFilter: {
+ "command.query.$comment": "currentop_query",
+ "command.collation": {locale: "fr"}
+ }
+ },
+ {
+ test: function(db) {
+ assert.commandWorked(
+ db.currentop_query.mapReduce(() => {},
+ (a, b) => {},
+ {
+ query: {$comment: "currentop_query"},
+ out: {inline: 1},
+ }));
+ },
+ command: "mapreduce",
+ planSummary: "COLLSCAN",
+ currentOpFilter: {
+ "command.query.$comment": "currentop_query",
+ "ns": /^currentop_query.*currentop_query/
+ }
+ },
+ {
+ test: function(db) {
+ assert.writeOK(db.currentop_query.remove({a: 2, $comment: "currentop_query"},
+ {collation: {locale: "fr"}}));
+ },
+ operation: "remove",
+ planSummary: "COLLSCAN",
+ currentOpFilter:
+ (isLocalMongosCurOp
+ ? {"command.delete": coll.getName(), "command.ordered": true}
+ : {
+ "command.q.$comment": "currentop_query",
+ "command.collation": {locale: "fr"}
+ })
+ },
+ {
+ test: function(db) {
+ assert.writeOK(
+ db.currentop_query.update({a: 1, $comment: "currentop_query"},
+ {$inc: {b: 1}},
+ {collation: {locale: "fr"}, multi: true}));
+ },
+ operation: "update",
+ planSummary: "COLLSCAN",
+ currentOpFilter:
+ (isLocalMongosCurOp
+ ? {"command.update": coll.getName(), "command.ordered": true}
+ : {
+ "command.q.$comment": "currentop_query",
+ "command.collation": {locale: "fr"}
+ })
+ }
+ ];
+
+ // The 'group' command cannot be run on a sharded collection.
+ if (!FixtureHelpers.isMongos(coll.getDB())) {
+ testList.push({
+ test: function(db) {
+ assert.eq(db.currentop_query.group({
+ key: {a: 1},
+ cond: {a: 1, $comment: "currentop_query"},
+ reduce: function() {},
+ initial: {},
+ collation: {locale: "fr"}
+ }),
+ [{"a": 1}]);
+ },
+ command: "group",
+ planSummary: "COLLSCAN",
+ currentOpFilter: {
+ "command.group.cond.$comment": "currentop_query",
+ "command.group.collation": {locale: "fr"}
+ }
+ });
}
- ];
- // The 'group' command cannot be run on a sharded collection.
- if (!FixtureHelpers.isMongos(coll.getDB())) {
- testList.push({
+ testList.forEach(confirmCurrentOpContents);
+
+ //
+ // Confirm currentOp contains collation for find command.
+ //
+ if (readMode === "commands") {
+ confirmCurrentOpContents({
+ test: function(db) {
+ assert.eq(db.currentop_query.find({a: 1})
+ .comment("currentop_query")
+ .collation({locale: "fr"})
+ .itcount(),
+ 1);
+ },
+ command: "find",
+ planSummary: "COLLSCAN",
+ currentOpFilter: {
+ "command.comment": "currentop_query",
+ "command.collation": {locale: "fr"}
+ }
+ });
+ }
+
+ //
+ // Confirm currentOp content for geoNear.
+ //
+ dropAndRecreateTestCollection();
+ for (let i = 0; i < 10; ++i) {
+ assert.writeOK(coll.insert({a: i, loc: {type: "Point", coordinates: [i, i]}}));
+ }
+ assert.commandWorked(coll.createIndex({loc: "2dsphere"}));
+
+ confirmCurrentOpContents({
test: function(db) {
- assert.eq(db.currentop_query.group({
- key: {a: 1},
- cond: {a: 1, $comment: "currentop_query"},
- reduce: function() {},
- initial: {},
+ assert.commandWorked(db.runCommand({
+ geoNear: "currentop_query",
+ near: {type: "Point", coordinates: [1, 1]},
+ spherical: true,
+ query: {$comment: "currentop_query"},
collation: {locale: "fr"}
- }),
- [{"a": 1}]);
+ }));
},
- command: "group",
- planSummary: "COLLSCAN",
+ command: "geoNear",
+ planSummary: "GEO_NEAR_2DSPHERE { loc: \"2dsphere\" }",
currentOpFilter: {
- "command.group.cond.$comment": "currentop_query",
- "command.group.collation": {locale: "fr"}
+ "command.query.$comment": "currentop_query",
+ "command.collation": {locale: "fr"}
}
});
- }
-
- testList.forEach(confirmCurrentOpContents);
- //
- // Confirm currentOp contains collation for find command.
- //
- if (readMode === "commands") {
- confirmCurrentOpContents({
- test: function(db) {
- assert.eq(db.currentop_query.find({a: 1})
- .comment("currentop_query")
- .collation({locale: "fr"})
- .itcount(),
- 1);
- },
- command: "find",
- planSummary: "COLLSCAN",
- currentOpFilter:
- {"command.comment": "currentop_query", "command.collation": {locale: "fr"}}
- });
- }
-
- //
- // Confirm currentOp content for geoNear.
- //
- dropAndRecreateTestCollection();
- for (let i = 0; i < 10; ++i) {
- assert.writeOK(coll.insert({a: i, loc: {type: "Point", coordinates: [i, i]}}));
- }
- assert.commandWorked(coll.createIndex({loc: "2dsphere"}));
-
- confirmCurrentOpContents({
- test: function(db) {
- assert.commandWorked(db.runCommand({
- geoNear: "currentop_query",
- near: {type: "Point", coordinates: [1, 1]},
- spherical: true,
- query: {$comment: "currentop_query"},
- collation: {locale: "fr"}
- }));
- },
- command: "geoNear",
- planSummary: "GEO_NEAR_2DSPHERE { loc: \"2dsphere\" }",
- currentOpFilter: {
- "command.query.$comment": "currentop_query",
- "command.collation": {locale: "fr"}
+ //
+ // Confirm currentOp content for getMore. This case tests command and legacy getMore
+ // with originating find and aggregate commands.
+ //
+ dropAndRecreateTestCollection();
+ for (let i = 0; i < 10; ++i) {
+ assert.writeOK(coll.insert({a: i}));
}
- });
- //
- // Confirm currentOp content for getMore. This case tests command and legacy getMore with
- // originating find and aggregate commands.
- //
- dropAndRecreateTestCollection();
- for (let i = 0; i < 10; ++i) {
- assert.writeOK(coll.insert({a: i}));
- }
+ const originatingCommands = {
+ find:
+ {find: "currentop_query", filter: {}, comment: "currentop_query", batchSize: 0},
+ aggregate: {
+ aggregate: "currentop_query",
+ pipeline: [{$match: {}}],
+ comment: "currentop_query",
+ cursor: {batchSize: 0}
+ }
+ };
- const originatingCommands = {
- find: {find: "currentop_query", filter: {}, comment: "currentop_query", batchSize: 0},
- aggregate: {
- aggregate: "currentop_query",
- pipeline: [{$match: {}}],
- comment: "currentop_query",
- cursor: {batchSize: 0}
+ for (let cmdName in originatingCommands) {
+ const cmdObj = originatingCommands[cmdName];
+ const cmdRes = testDB.runCommand(cmdObj);
+ assert.commandWorked(cmdRes);
+
+ TestData.commandResult = cmdRes;
+
+ // If this is a non-localOps test running via mongoS, then the cursorID we obtained
+ // above is the ID of the mongoS cursor, and will not match the IDs of any of the
+ // individual shard cursors in the currentOp output. We therefore don't perform an
+ // exact match on 'command.getMore', but only verify that the cursor ID is non-zero.
+ const filter = {
+ "command.getMore":
+ (isRemoteShardCurOp ? {$gt: 0} : TestData.commandResult.cursor.id),
+ [`originatingCommand.${cmdName}`]:
+ {$exists: true}, "originatingCommand.comment": "currentop_query"
+ };
+
+ confirmCurrentOpContents({
+ test: function(db) {
+ const cursor = new DBCommandCursor(db, TestData.commandResult, 5);
+ assert.eq(cursor.itcount(), 10);
+ },
+ command: "getMore",
+ planSummary: "COLLSCAN",
+ currentOpFilter: filter
+ });
+
+ delete TestData.commandResult;
}
- };
- for (let cmdName in originatingCommands) {
- const cmdObj = originatingCommands[cmdName];
- const cmdRes = testDB.runCommand(cmdObj);
- assert.commandWorked(cmdRes);
+ //
+ // Confirm that currentOp displays upconverted getMore and originatingCommand in the
+ // case of a legacy query.
+ //
+ if (readMode === "legacy") {
+ let filter = {
+ "command.getMore": {$gt: 0},
+ "command.collection": "currentop_query",
+ "command.batchSize": 2,
+ "originatingCommand.find": "currentop_query",
+ "originatingCommand.ntoreturn": 2,
+ "originatingCommand.comment": "currentop_query"
+ };
+
+ confirmCurrentOpContents({
+ test: function(db) {
+ load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers.
+
+ // Temporarily disable hanging yields so that we can iterate the first
+ // batch.
+ FixtureHelpers.runCommandOnEachPrimary({
+ db: db.getSiblingDB("admin"),
+ cmdObj: {configureFailPoint: "setYieldAllLocksHang", mode: "off"}
+ });
- TestData.commandResult = cmdRes;
+ let cursor =
+ db.currentop_query.find({}).comment("currentop_query").batchSize(2);
- // If this is a non-localOps test running via mongoS, then the cursorID we obtained
- // above is the ID of the mongoS cursor, and will not match the IDs of any of the
- // individual shard cursors in the currentOp output. We therefore don't perform an exact
- // match on 'command.getMore', but simply verify that the cursor ID is non-zero.
- const filter = {
- "command.getMore":
- (isRemoteShardCurOp ? {$gt: 0} : TestData.commandResult.cursor.id),
- [`originatingCommand.${cmdName}`]:
- {$exists: true}, "originatingCommand.comment": "currentop_query"
- };
+ // Exhaust the current batch so that the next request will force a getMore.
+ while (cursor.objsLeftInBatch() > 0) {
+ cursor.next();
+ }
- confirmCurrentOpContents({
- test: function(db) {
- const cursor = new DBCommandCursor(db, TestData.commandResult, 5);
- assert.eq(cursor.itcount(), 10);
- },
- command: "getMore",
- planSummary: "COLLSCAN",
- currentOpFilter: filter
- });
+ // Set yields to hang so that we can check currentOp output.
+ FixtureHelpers.runCommandOnEachPrimary({
+ db: db.getSiblingDB("admin"),
+ cmdObj:
+ {configureFailPoint: "setYieldAllLocksHang", mode: "alwaysOn"}
+ });
- delete TestData.commandResult;
+ assert.eq(cursor.itcount(), 8);
+ },
+ operation: "getmore",
+ planSummary: "COLLSCAN",
+ currentOpFilter: filter
+ });
+ }
+
+ //
+ // Confirm that a legacy query whose filter contains a field named 'query' appears as
+ // expected in currentOp. This test ensures that upconverting a legacy query correctly
+ // identifies this as a user field rather than a wrapped filter spec.
+ //
+ if (readMode === "legacy") {
+ confirmCurrentOpContents({
+ test: function(db) {
+ assert.eq(
+ db.currentop_query.find({query: "foo", $comment: "currentop_query"})
+ .itcount(),
+ 0);
+ },
+ command: "find",
+ planSummary: "COLLSCAN",
+ currentOpFilter: {
+ "command.filter.$comment": "currentop_query",
+ "command.filter.query": "foo"
+ }
+ });
+ }
}
- //
- // Confirm that currentOp displays upconverted getMore and originatingCommand in the case of
- // a legacy query.
- //
- if (readMode === "legacy") {
- let filter = {
- "command.getMore": {$gt: 0},
- "command.collection": "currentop_query",
- "command.batchSize": 2,
- "originatingCommand.find": "currentop_query",
- "originatingCommand.ntoreturn": 2,
- "originatingCommand.comment": "currentop_query"
+ /**
+ * Runs a set of tests to verify that currentOp will serialize objects exceeding ~1000 bytes
+ * to string when the 'truncateOps' parameter is set.
+ */
+ function runTruncationTests() {
+ dropAndRecreateTestCollection();
+ assert.writeOK(coll.insert({a: 1}));
+
+ // When the currentOp command serializes the query object as a string, individual string
+ // values inside it are truncated at 150 characters. To test "total length" truncation
+ // we need to pass multiple values, each smaller than 150 bytes.
+ TestData.queryFilter = {
+ "1": "1".repeat(149),
+ "2": "2".repeat(149),
+ "3": "3".repeat(149),
+ "4": "4".repeat(149),
+ "5": "5".repeat(149),
+ "6": "6".repeat(149),
+ "7": "7".repeat(149),
};
- confirmCurrentOpContents({
- test: function(db) {
- load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers.
+ var truncatedQueryString = "^\\{ find: \"currentop_query\", filter: \\{ " +
+ "1: \"1{149}\", 2: \"2{149}\", 3: \"3{149}\", 4: \"4{149}\", 5: \"5{149}\", " +
+ "6: \"6{149}\", 7: \"7+\\.\\.\\.";
- // Temporarily disable hanging yields so that we can iterate the first batch.
- FixtureHelpers.runCommandOnEachPrimary({
- db: db.getSiblingDB("admin"),
- cmdObj: {configureFailPoint: "setYieldAllLocksHang", mode: "off"}
- });
+ let currentOpFilter;
- let cursor =
- db.currentop_query.find({}).comment("currentop_query").batchSize(2);
-
- // Exhaust the current batch so that the next request will force a getMore.
- while (cursor.objsLeftInBatch() > 0) {
- cursor.next();
- }
-
- // Set yields to hang so that we can check currentOp output.
- FixtureHelpers.runCommandOnEachPrimary({
- db: db.getSiblingDB("admin"),
- cmdObj: {configureFailPoint: "setYieldAllLocksHang", mode: "alwaysOn"}
- });
-
- assert.eq(cursor.itcount(), 8);
- },
- operation: "getmore",
- planSummary: "COLLSCAN",
- currentOpFilter: filter
- });
- }
+ currentOpFilter = {
+ "command.$truncated": {$regex: truncatedQueryString},
+ "command.comment": "currentop_query"
+ };
- //
- // Confirm that a legacy query whose filter contains a field named 'query' appears as
- // expected in currentOp. This test ensures that upconverting a legacy query correctly
- // identifies this as a user field rather than a wrapped filter spec.
- //
- if (readMode === "legacy") {
confirmCurrentOpContents({
test: function(db) {
- assert.eq(db.currentop_query.find({query: "foo", $comment: "currentop_query"})
+ assert.eq(db.currentop_query.find(TestData.queryFilter)
+ .comment("currentop_query")
.itcount(),
0);
},
- command: "find",
planSummary: "COLLSCAN",
- currentOpFilter:
- {"command.filter.$comment": "currentop_query", "command.filter.query": "foo"}
+ currentOpFilter: currentOpFilter
});
- }
-
- //
- // Confirm ~1000 byte size limit for query field in the case of the currentOp command. For
- // $currentOp aggregations, this limit does not apply.
- //
- dropAndRecreateTestCollection();
- assert.writeOK(coll.insert({a: 1}));
-
- // When the currentOp command serializes the query object as a string, individual string
- // values inside it are truncated at 150 characters. To test "total length" truncation we
- // need to pass multiple values, each smaller than 150 bytes.
- TestData.queryFilter = {
- "1": "1".repeat(149),
- "2": "2".repeat(149),
- "3": "3".repeat(149),
- "4": "4".repeat(149),
- "5": "5".repeat(149),
- "6": "6".repeat(149),
- "7": "7".repeat(149),
- };
-
- var truncatedQueryString = "^\\{ find: \"currentop_query\", filter: \\{ " +
- "1: \"1{149}\", 2: \"2{149}\", 3: \"3{149}\", 4: \"4{149}\", 5: \"5{149}\", " +
- "6: \"6{149}\", 7: \"7+\\.\\.\\.";
- let currentOpFilter;
-
- if (truncatedOps) {
- currentOpFilter = {
- "command.$truncated": {$regex: truncatedQueryString},
- "command.comment": "currentop_query"
- };
- } else {
- currentOpFilter = {
- "command.filter": TestData.queryFilter,
- "command.comment": "currentop_query"
- };
- }
+ // Verify that an originatingCommand truncated by currentOp appears as { $truncated:
+ // <string>, comment: <string> }.
+ const cmdRes = testDB.runCommand({
+ find: "currentop_query",
+ filter: TestData.queryFilter,
+ comment: "currentop_query",
+ batchSize: 0
+ });
+ assert.commandWorked(cmdRes);
- confirmCurrentOpContents({
- test: function(db) {
- assert.eq(db.currentop_query.find(TestData.queryFilter)
- .comment("currentop_query")
- .itcount(),
- 0);
- },
- planSummary: "COLLSCAN",
- currentOpFilter: currentOpFilter
- });
-
- // Verify that an originatingCommand truncated by currentOp appears as { $truncated:
- // <string>, comment: <string> }.
- const cmdRes = testDB.runCommand({
- find: "currentop_query",
- filter: TestData.queryFilter,
- comment: "currentop_query",
- batchSize: 0
- });
- assert.commandWorked(cmdRes);
-
- TestData.commandResult = cmdRes;
+ TestData.commandResult = cmdRes;
- if (truncatedOps) {
currentOpFilter = {
"command.getMore":
(isRemoteShardCurOp ? {$gt: 0} : TestData.commandResult.cursor.id),
"originatingCommand.$truncated": {$regex: truncatedQueryString},
"originatingCommand.comment": "currentop_query"
};
- } else {
- currentOpFilter = {
- "command.getMore":
- (isRemoteShardCurOp ? {$gt: 0} : TestData.commandResult.cursor.id),
- "originatingCommand.filter": TestData.queryFilter,
- "originatingCommand.comment": "currentop_query"
- };
- }
- confirmCurrentOpContents({
- test: function(db) {
- var cursor = new DBCommandCursor(db, TestData.commandResult, 5);
- assert.eq(cursor.itcount(), 0);
- },
- planSummary: "COLLSCAN",
- currentOpFilter: currentOpFilter
- });
+ confirmCurrentOpContents({
+ test: function(db) {
+ var cursor = new DBCommandCursor(db, TestData.commandResult, 5);
+ assert.eq(cursor.itcount(), 0);
+ },
+ planSummary: "COLLSCAN",
+ currentOpFilter: currentOpFilter
+ });
- delete TestData.commandResult;
+ delete TestData.commandResult;
- // Verify that an aggregation truncated by currentOp appears as { $truncated: <string>,
- // comment: <string> } when a comment parameter is present.
- truncatedQueryString =
- "^\\{ aggregate: \"currentop_query\", pipeline: \\[ \\{ \\$match: \\{ " +
- "1: \"1{149}\", 2: \"2{149}\", 3: \"3{149}\", 4: \"4{149}\", 5: \"5{149}\", " +
- "6: \"6{149}\", 7: \"7+\\.\\.\\.";
+ // Verify that an aggregation truncated by currentOp appears as { $truncated: <string>,
+ // comment: <string> } when a comment parameter is present.
+ truncatedQueryString =
+ "^\\{ aggregate: \"currentop_query\", pipeline: \\[ \\{ \\$match: \\{ " +
+ "1: \"1{149}\", 2: \"2{149}\", 3: \"3{149}\", 4: \"4{149}\", 5: \"5{149}\", " +
+ "6: \"6{149}\", 7: \"7+\\.\\.\\.";
- if (truncatedOps) {
currentOpFilter = commandOrOriginatingCommand(
{"$truncated": {$regex: truncatedQueryString}, "comment": "currentop_query"},
isRemoteShardCurOp);
- } else {
- currentOpFilter = commandOrOriginatingCommand(
- {"pipeline.0.$match": TestData.queryFilter, "comment": "currentop_query"},
- isRemoteShardCurOp);
- }
- confirmCurrentOpContents({
- test: function(db) {
- assert.eq(
- db.currentop_query
- .aggregate([{$match: TestData.queryFilter}], {comment: "currentop_query"})
- .itcount(),
- 0);
- },
- planSummary: "COLLSCAN",
- currentOpFilter: currentOpFilter
- });
-
- delete TestData.queryFilter;
+ confirmCurrentOpContents({
+ test: function(db) {
+ assert.eq(db.currentop_query
+ .aggregate([{$match: TestData.queryFilter}],
+ {comment: "currentop_query"})
+ .itcount(),
+ 0);
+ },
+ planSummary: "COLLSCAN",
+ currentOpFilter: currentOpFilter
+ });
+
+ delete TestData.queryFilter;
+ }
}
- function currentOpCommand(inputDB, filter, localOps) {
- return inputDB.currentOp(filter);
+ function currentOpCommand(inputDB, filter, truncatedOps, localOps) {
+ return inputDB.currentOp(Object.assign(filter, {$truncateOps: truncatedOps}));
}
- function currentOpAgg(inputDB, filter, localOps) {
+ function currentOpAgg(inputDB, filter, truncatedOps, localOps) {
return {
- inprog:
- inputDB.getSiblingDB("admin")
- .aggregate([{$currentOp: {localOps: (localOps || false)}}, {$match: filter}])
- .toArray(),
+ inprog: inputDB.getSiblingDB("admin")
+ .aggregate([
+ {
+ $currentOp: {
+ localOps: (localOps || false),
+ truncateOps: (truncatedOps || false)
+ }
+ },
+ {$match: filter}
+ ])
+ .toArray(),
ok: 1
};
}
for (let connType of[rsConn, mongosConn]) {
for (let readMode of["commands", "legacy"]) {
- for (let localOps of[false, true]) {
- // Run all tests using the $currentOp aggregation stage.
+ for (let truncatedOps of[false, true]) {
+ for (let localOps of[false, true]) {
+ // Run all tests using the $currentOp aggregation stage.
+ runTests({
+ conn: connType,
+ readMode: readMode,
+ currentOp: currentOpAgg,
+ localOps: localOps,
+ truncatedOps: truncatedOps
+ });
+ }
+ // Run tests using the currentOp command. The 'localOps' parameter is not supported.
runTests({
conn: connType,
readMode: readMode,
- currentOp: currentOpAgg,
- localOps: localOps
+ currentOp: currentOpCommand,
+ localOps: false,
+ truncatedOps: truncatedOps
});
}
- // Run all tests using the currentOp command. The 'localOps' parameter is not supported.
- runTests({
- conn: connType,
- readMode: readMode,
- currentOp: currentOpCommand,
- truncatedOps: true
- });
}
}
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 0d8dac60721..a6beb415bf0 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -381,11 +381,14 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/db/commands',
+ ],
+ LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/namespace_string',
'$BUILD_DIR/mongo/db/pipeline/aggregation_request',
'$BUILD_DIR/mongo/db/query/command_request_response',
'$BUILD_DIR/mongo/db/service_context',
- ]
+ 'test_commands_enabled'
+ ],
)
env.Library(
diff --git a/src/mongo/db/commands/current_op_common.cpp b/src/mongo/db/commands/current_op_common.cpp
index bd38222f0c6..cc94eeacd94 100644
--- a/src/mongo/db/commands/current_op_common.cpp
+++ b/src/mongo/db/commands/current_op_common.cpp
@@ -32,12 +32,20 @@
#include "mongo/db/commands/current_op_common.h"
+#include <boost/container/flat_set.hpp>
#include <string>
#include "mongo/db/command_generic_argument.h"
+#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/namespace_string.h"
namespace mongo {
+namespace {
+static constexpr auto kAll = "$all"_sd;
+static constexpr auto kOwnOps = "$ownOps"_sd;
+static constexpr auto kTruncateOps = "$truncateOps"_sd;
+static const boost::container::flat_set<StringData> kCurOpCmdParams = {kAll, kOwnOps, kTruncateOps};
+} // namespace
bool CurrentOpCommandBase::run(OperationContext* opCtx,
const std::string& dbName,
@@ -53,9 +61,15 @@ bool CurrentOpCommandBase::run(OperationContext* opCtx,
BSONObjBuilder currentOpBuilder;
BSONObjBuilder currentOpSpecBuilder(currentOpBuilder.subobjStart("$currentOp"));
- currentOpSpecBuilder.append("idleConnections", cmdObj["$all"].trueValue());
- currentOpSpecBuilder.append("allUsers", !cmdObj["$ownOps"].trueValue());
- currentOpSpecBuilder.append("truncateOps", true);
+ // If test commands are enabled, then we allow the currentOp commands to specify whether or not
+ // to truncate long operations via the '$truncateOps' parameter. Otherwise, we always truncate
+ // operations to match the behaviour of the legacy currentOp command.
+ const bool truncateOps =
+ !getTestCommandsEnabled() || !cmdObj[kTruncateOps] || cmdObj[kTruncateOps].trueValue();
+
+ currentOpSpecBuilder.append("idleConnections", cmdObj[kAll].trueValue());
+ currentOpSpecBuilder.append("allUsers", !cmdObj[kOwnOps].trueValue());
+ currentOpSpecBuilder.append("truncateOps", truncateOps);
currentOpSpecBuilder.doneFast();
pipeline.push_back(currentOpBuilder.obj());
@@ -68,8 +82,7 @@ bool CurrentOpCommandBase::run(OperationContext* opCtx,
for (const auto& elt : cmdObj) {
const auto fieldName = elt.fieldNameStringData();
- if (0 == idx++ || fieldName == "$all" || fieldName == "$ownOps" ||
- isGenericArgument(fieldName)) {
+ if (0 == idx++ || kCurOpCmdParams.count(fieldName) || isGenericArgument(fieldName)) {
continue;
}