diff options
author | Bernard Gorman <bernard.gorman@gmail.com> | 2017-03-22 08:21:15 +0000 |
---|---|---|
committer | Bernard Gorman <bernard.gorman@gmail.com> | 2017-03-22 22:30:31 +0000 |
commit | 51d8b9c2f5eafc457f889d9786ebd68e4398ba64 (patch) | |
tree | 5f03f06ee26558803de1f8c7f40a6a6a7e5c09f7 | |
parent | 7b4770c1dbbc2a264a10f4fcfc87025ce832b680 (diff) | |
download | mongo-51d8b9c2f5eafc457f889d9786ebd68e4398ba64.tar.gz |
SERVER-27439 Large queries can omit comment in currentOp
-rw-r--r-- | jstests/core/profile2.js | 16 | ||||
-rw-r--r-- | jstests/core/profile_agg.js | 15 | ||||
-rw-r--r-- | jstests/core/profile_find.js | 15 | ||||
-rw-r--r-- | jstests/core/profile_getmore.js | 22 | ||||
-rw-r--r-- | jstests/noPassthrough/currentop_query.js | 61 | ||||
-rw-r--r-- | src/mongo/db/curop.cpp | 67 |
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()) { |