summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernard Gorman <bernard.gorman@gmail.com>2017-03-22 08:21:15 +0000
committerBernard Gorman <bernard.gorman@gmail.com>2017-03-22 22:30:31 +0000
commit51d8b9c2f5eafc457f889d9786ebd68e4398ba64 (patch)
tree5f03f06ee26558803de1f8c7f40a6a6a7e5c09f7
parent7b4770c1dbbc2a264a10f4fcfc87025ce832b680 (diff)
downloadmongo-51d8b9c2f5eafc457f889d9786ebd68e4398ba64.tar.gz
SERVER-27439 Large queries can omit comment in currentOp
-rw-r--r--jstests/core/profile2.js16
-rw-r--r--jstests/core/profile_agg.js15
-rw-r--r--jstests/core/profile_find.js15
-rw-r--r--jstests/core/profile_getmore.js22
-rw-r--r--jstests/noPassthrough/currentop_query.js61
-rw-r--r--src/mongo/db/curop.cpp67
6 files changed, 173 insertions, 23 deletions
diff --git a/jstests/core/profile2.js b/jstests/core/profile2.js
index bb1605abd1e..6401222ca1d 100644
--- a/jstests/core/profile2.js
+++ b/jstests/core/profile2.js
@@ -22,9 +22,9 @@ var result = results[0];
assert(result.hasOwnProperty('ns'));
assert(result.hasOwnProperty('millis'));
assert(result.hasOwnProperty('query'));
-assert.eq('string', typeof(result.query));
+assert.eq('string', typeof(result.query.$truncated));
// String value is truncated.
-assert(result.query.match(/filter: { a: "a+\.\.\." } }$/));
+assert(result.query.$truncated.match(/filter: { a: "a+\.\.\." } }$/));
assert.commandWorked(coll.getDB().runCommand({profile: 0}));
coll.getDB().system.profile.drop();
@@ -38,8 +38,8 @@ var result = results[0];
assert(result.hasOwnProperty('ns'));
assert(result.hasOwnProperty('millis'));
assert(result.hasOwnProperty('query'));
-assert.eq('string', typeof(result.query));
-assert(result.query.match(/^{ a: "a+\.\.\." }$/)); // String value is truncated.
+assert.eq('string', typeof(result.query.$truncated));
+assert(result.query.$truncated.match(/^{ a: "a+\.\.\." }$/)); // String value is truncated.
assert.commandWorked(coll.getDB().runCommand({profile: 0}));
coll.getDB().system.profile.drop();
@@ -53,8 +53,8 @@ var result = results[0];
assert(result.hasOwnProperty('ns'));
assert(result.hasOwnProperty('millis'));
assert(result.hasOwnProperty('updateobj'));
-assert.eq('string', typeof(result.updateobj));
-assert(result.updateobj.match(/^{ a: "a+\.\.\." }$/)); // String value is truncated.
+assert.eq('string', typeof(result.updateobj.$truncated));
+assert(result.updateobj.$truncated.match(/^{ a: "a+\.\.\." }$/)); // String value is truncated.
assert.commandWorked(coll.getDB().runCommand({profile: 0}));
coll.getDB().system.profile.drop();
@@ -72,8 +72,8 @@ var result = results[0];
assert(result.hasOwnProperty('ns'));
assert(result.hasOwnProperty('millis'));
assert(result.hasOwnProperty('query'));
-assert.eq('string', typeof(result.query));
+assert.eq('string', typeof(result.query.$truncated));
// Query object itself is truncated.
-assert(result.query.match(/filter: { a0: 1\.0, a1: .*\.\.\.$/));
+assert(result.query.$truncated.match(/filter: { a0: 1\.0, a1: .*\.\.\.$/));
assert.commandWorked(coll.getDB().runCommand({profile: 0}));
diff --git a/jstests/core/profile_agg.js b/jstests/core/profile_agg.js
index 262fa29495b..9bd60249830 100644
--- a/jstests/core/profile_agg.js
+++ b/jstests/core/profile_agg.js
@@ -86,4 +86,19 @@
assert.eq(1, coll.aggregate([{$match: {a: 3, b: 3}}], {hint: {_id: 1}}).itcount());
profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.command.hint, {_id: 1}, tojson(profileObj));
+
+ //
+ // Confirm that aggregations are truncated in the profiler as { $truncated: <string>, comment:
+ // <string> } when a comment parameter is provided.
+ //
+ let matchPredicate = {};
+
+ for (let i = 0; i < 501; i++) {
+ matchPredicate[i] = "a".repeat(150);
+ }
+
+ assert.eq(coll.aggregate([{$match: matchPredicate}], {comment: "profile_agg"}).itcount(), 0);
+ profileObj = getLatestProfilerEntry(testDB);
+ assert.eq((typeof profileObj.command.$truncated), "string", tojson(profileObj));
+ assert.eq(profileObj.command.comment, "profile_agg", tojson(profileObj));
})();
diff --git a/jstests/core/profile_find.js b/jstests/core/profile_find.js
index 36116c5a6fa..d49aa30bc9f 100644
--- a/jstests/core/profile_find.js
+++ b/jstests/core/profile_find.js
@@ -154,4 +154,19 @@
assert.eq(coll.find().snapshot().itcount(), 1);
profileObj = getLatestProfilerEntry(testDB, profileEntryFilter);
assert.eq(profileObj.query.snapshot, true, tojson(profileObj));
+
+ //
+ // Confirm that queries are truncated in the profiler as { $truncated: <string>, comment:
+ // <string> }
+ //
+ let queryPredicate = {};
+
+ for (let i = 0; i < 501; i++) {
+ queryPredicate[i] = "a".repeat(150);
+ }
+
+ assert.eq(coll.find(queryPredicate).comment("profile_find").itcount(), 0);
+ profileObj = getLatestProfilerEntry(testDB, profileEntryFilter);
+ assert.eq((typeof profileObj.query.$truncated), "string", tojson(profileObj));
+ assert.eq(profileObj.query.comment, "profile_find", tojson(profileObj));
})();
diff --git a/jstests/core/profile_getmore.js b/jstests/core/profile_getmore.js
index a72f0ea236f..38dc30434af 100644
--- a/jstests/core/profile_getmore.js
+++ b/jstests/core/profile_getmore.js
@@ -121,4 +121,26 @@
assert.eq(profileObj.docsExamined, 20, tojson(profileObj));
assert.eq(profileObj.appName, "MongoDB Shell", tojson(profileObj));
assert.eq(profileObj.originatingCommand.hint, {a: 1}, tojson(profileObj));
+
+ //
+ // Confirm that originatingCommand is truncated in the profiler as { $truncated: <string>,
+ // comment: <string> }
+ //
+ let docToInsert = {};
+
+ for (i = 0; i < 501; i++) {
+ docToInsert[i] = "a".repeat(150);
+ }
+
+ coll.drop();
+ for (i = 0; i < 4; i++) {
+ assert.writeOK(coll.insert(docToInsert));
+ }
+
+ cursor = coll.find(docToInsert).comment("profile_getmore").batchSize(2);
+ assert.eq(cursor.itcount(), 4); // Consume result set and trigger getMore.
+
+ profileObj = getLatestProfilerEntry(testDB);
+ assert.eq((typeof profileObj.originatingCommand.$truncated), "string", tojson(profileObj));
+ assert.eq(profileObj.originatingCommand.comment, "profile_getmore", tojson(profileObj));
})();
diff --git a/jstests/noPassthrough/currentop_query.js b/jstests/noPassthrough/currentop_query.js
index 7137a9bdd18..5de8ce8abaf 100644
--- a/jstests/noPassthrough/currentop_query.js
+++ b/jstests/noPassthrough/currentop_query.js
@@ -365,6 +365,7 @@
"5": "5".repeat(100),
"6": "6".repeat(100),
};
+
var truncatedQueryString = "{ find: \"currentop_query\", filter: { " +
"1: \"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\", " +
"2: \"2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222\", " +
@@ -374,10 +375,66 @@
confirmCurrentOpContents({
test: function() {
- assert.eq(db.currentop_query.find(TestData.queryFilter).itcount(), 0);
+ assert.eq(db.currentop_query.find(TestData.queryFilter)
+ .comment("currentop_query")
+ .itcount(),
+ 0);
+ },
+ planSummary: "COLLSCAN",
+ currentOpFilter:
+ {"query.$truncated": truncatedQueryString, "query.comment": "currentop_query"}
+ });
+
+ // Verify that an originatingCommand truncated by currentOp appears as { $truncated:
+ // <string>, comment: <string> }.
+ cmdRes = testDB.runCommand({
+ find: "currentop_query",
+ filter: TestData.queryFilter,
+ comment: "currentop_query",
+ batchSize: 0
+ });
+ assert.commandWorked(cmdRes);
+
+ TestData.commandResult = cmdRes;
+
+ filter = {
+ "query.getMore": TestData.commandResult.cursor.id,
+ "originatingCommand.$truncated": truncatedQueryString,
+ "originatingCommand.comment": "currentop_query"
+ };
+
+ confirmCurrentOpContents({
+ test: function() {
+ var cursor = new DBCommandCursor(db.getMongo(), TestData.commandResult, 5);
+ assert.eq(cursor.itcount(), 0);
+ },
+ planSummary: "COLLSCAN",
+ currentOpFilter: filter
+ });
+
+ 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: " +
+ "\"111111111111111111111111111111111111111111111111111111111111111111111111111111" +
+ "1111111111111111111111\", 2: \"2222222222222222222222222222222222222222222222222" +
+ "222222222222222222222222222222222222222222222222222\", 3: \"33333333333333333333" +
+ "33333333333333333333333333333333333333333333333333333333333333333333333333333333" +
+ "\", 4: \"44444444444444444444444444444444444444444444444444444444444444444444444" +
+ "44444444444444444444444444444\", 5: \"555555555555555555555...";
+
+ confirmCurrentOpContents({
+ test: function() {
+ assert.eq(
+ db.currentop_query
+ .aggregate([{$match: TestData.queryFilter}], {comment: "currentop_query"})
+ .itcount(),
+ 0);
},
planSummary: "COLLSCAN",
- currentOpFilter: {"query": truncatedQueryString}
+ currentOpFilter:
+ {"query.$truncated": truncatedQueryString, "query.comment": "currentop_query"}
});
delete TestData.queryFilter;
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index 2af0907170d..f4c028879c3 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -296,23 +296,30 @@ Command::ReadWriteType CurOp::getReadWriteType() const {
}
namespace {
+
+/**
+ * Used by callers of appendAsObjOrString to indicate whether a comment parameter may be present and
+ * should be retained upon truncation.
+ */
+enum class TruncationMode { kNoComment, kIncludeComment };
+
/**
- * Appends {name: obj} to the provided builder. If obj is greater than maxSize, appends a
- * string summary of obj instead of the object itself.
+ * Appends {<name>: obj} to the provided builder. If obj is greater than maxSize, appends a string
+ * summary of obj as { <name>: { $truncated: "obj" } }. If a comment parameter is present, add it to
+ * the truncation object.
*/
void appendAsObjOrString(StringData name,
const BSONObj& obj,
size_t maxSize,
- BSONObjBuilder* builder) {
+ BSONObjBuilder* builder,
+ TruncationMode truncateBehavior = TruncationMode::kNoComment) {
if (static_cast<size_t>(obj.objsize()) <= maxSize) {
builder->append(name, obj);
} else {
// Generate an abbreviated serialization for the object, by passing false as the
// "full" argument to obj.toString().
std::string objToString = obj.toString();
- if (objToString.size() <= maxSize) {
- builder->append(name, objToString);
- } else {
+ if (objToString.size() > maxSize) {
// objToString is still too long, so we append to the builder a truncated form
// of objToString concatenated with "...". Instead of creating a new string
// temporary, mutate objToString to do this (we know that we can mutate
@@ -320,8 +327,22 @@ void appendAsObjOrString(StringData name,
objToString[maxSize - 3] = '.';
objToString[maxSize - 2] = '.';
objToString[maxSize - 1] = '.';
- builder->append(name, StringData(objToString).substr(0, maxSize));
}
+
+ StringData truncation = StringData(objToString).substr(0, maxSize);
+
+ // Append the truncated representation of the object to the builder. If this is an operation
+ // which supports a comment parameter and one is present, write it to the object alongside
+ // the truncated op. This object will appear as {$truncated: "{find: \"collection\", filter:
+ // {x: 1, ...", comment: "comment text" }
+ BSONObjBuilder truncatedBuilder(builder->subobjStart(name));
+ truncatedBuilder.append("$truncated", truncation);
+
+ if (truncateBehavior == TruncationMode::kIncludeComment && obj["comment"]) {
+ truncatedBuilder.append(obj["comment"]);
+ }
+
+ truncatedBuilder.doneFast();
}
}
} // namespace
@@ -354,9 +375,15 @@ void CurOp::reportState(BSONObjBuilder* builder) {
appendAsObjOrString("query",
upconvertQueryEntry(_query, NamespaceString(_ns), ntoreturn, ntoskip),
maxQuerySize,
- builder);
+ builder,
+ TruncationMode::kIncludeComment);
} else {
- appendAsObjOrString("query", _query, maxQuerySize, builder);
+ appendAsObjOrString(
+ "query",
+ _query,
+ maxQuerySize,
+ builder,
+ (_isCommand ? TruncationMode::kIncludeComment : TruncationMode::kNoComment));
}
if (!_collation.isEmpty()) {
@@ -364,7 +391,11 @@ void CurOp::reportState(BSONObjBuilder* builder) {
}
if (!_originatingCommand.isEmpty()) {
- appendAsObjOrString("originatingCommand", _originatingCommand, maxQuerySize, builder);
+ appendAsObjOrString("originatingCommand",
+ _originatingCommand,
+ maxQuerySize,
+ builder,
+ TruncationMode::kIncludeComment);
}
if (!_planSummary.empty()) {
@@ -557,15 +588,25 @@ void OpDebug::append(const CurOp& curop,
appendAsObjOrString("query",
upconvertQueryEntry(curop.query(), nss, ntoreturn, ntoskip),
maxElementSize,
- &b);
+ &b,
+ TruncationMode::kIncludeComment);
} else if (curop.haveQuery()) {
const char* fieldName = (logicalOp == LogicalOp::opCommand) ? "command" : "query";
- appendAsObjOrString(fieldName, curop.query(), maxElementSize, &b);
+ appendAsObjOrString(
+ fieldName,
+ curop.query(),
+ maxElementSize,
+ &b,
+ (iscommand ? TruncationMode::kIncludeComment : TruncationMode::kNoComment));
}
auto originatingCommand = curop.originatingCommand();
if (!originatingCommand.isEmpty()) {
- appendAsObjOrString("originatingCommand", originatingCommand, maxElementSize, &b);
+ appendAsObjOrString("originatingCommand",
+ originatingCommand,
+ maxElementSize,
+ &b,
+ TruncationMode::kIncludeComment);
}
if (!updateobj.isEmpty()) {