summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Wahlin <james.wahlin@10gen.com>2016-04-06 14:45:29 -0400
committerJames Wahlin <james.wahlin@10gen.com>2016-04-20 13:22:35 -0400
commit0f1cf1b71a12d2e41aeea064848ef192bfceddd7 (patch)
treea563d427ab4126a5fb05834f1d9dc9e6c0ed7ae6
parent5dd37d3fa197e73fab8298e5d748e06393b053b2 (diff)
downloadmongo-0f1cf1b71a12d2e41aeea064848ef192bfceddd7.tar.gz
SERVER-23257 Report keysExamined/docsExamined/hasSortStage where valid
-rw-r--r--jstests/core/profile_agg.js24
-rw-r--r--jstests/core/profile_count.js28
-rw-r--r--jstests/core/profile_delete.js8
-rw-r--r--jstests/core/profile_distinct.js12
-rw-r--r--jstests/core/profile_find.js1
-rw-r--r--jstests/core/profile_findandmodify.js65
-rw-r--r--jstests/core/profile_geonear.js7
-rw-r--r--jstests/core/profile_getmore.js27
-rw-r--r--jstests/core/profile_group.js8
-rw-r--r--jstests/core/profile_mapreduce.js22
-rw-r--r--jstests/core/profile_update.js7
-rw-r--r--src/mongo/db/commands/count_cmd.cpp4
-rw-r--r--src/mongo/db/commands/distinct.cpp4
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp7
-rw-r--r--src/mongo/db/commands/geo_near_cmd.cpp15
-rw-r--r--src/mongo/db/commands/getmore_cmd.cpp14
-rw-r--r--src/mongo/db/commands/group_cmd.cpp4
-rw-r--r--src/mongo/db/commands/mr.cpp8
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp7
-rw-r--r--src/mongo/db/commands/write_commands/batch_executor.cpp7
-rw-r--r--src/mongo/db/curop.cpp10
-rw-r--r--src/mongo/db/curop.h6
-rw-r--r--src/mongo/db/exec/update.cpp10
-rw-r--r--src/mongo/db/exec/update.h7
-rw-r--r--src/mongo/db/instance.cpp10
-rw-r--r--src/mongo/db/ops/update.cpp4
-rw-r--r--src/mongo/db/query/explain.cpp1
-rw-r--r--src/mongo/db/query/explain.h35
-rw-r--r--src/mongo/db/query/find.cpp20
-rw-r--r--src/mongo/db/query/plan_summary_stats.h68
-rw-r--r--src/mongo/dbtests/query_stage_multiplan.cpp1
31 files changed, 322 insertions, 129 deletions
diff --git a/jstests/core/profile_agg.js b/jstests/core/profile_agg.js
index 4bcec82d0c0..126a71d4d85 100644
--- a/jstests/core/profile_agg.js
+++ b/jstests/core/profile_agg.js
@@ -1,6 +1,5 @@
// Confirms that profiled aggregation execution contains all expected metrics with proper values.
-// TODO SERVER-23257: Add keysExamined, docsExamined.
-// TODO SERVER-23265: Add planSummary.
+// TODO SERVER-23265: Add planSummary. Fix keysExamined, docsExamined & hasSortStage.
// TODO SERVER-23264: Add execStats.
(function() {
@@ -23,12 +22,18 @@
for (i = 0; i < 10; ++i) {
assert.writeOK(coll.insert({a: i}));
}
+ assert.commandWorked(coll.createIndex({a: 1}));
assert.eq(8, coll.aggregate([{$match: {a: {$gte: 2}}}]).itcount());
var profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "command", tojson(profileObj));
+
+ // TODO SERVER-23265: keysExamined and docsExamined should be non-zero.
+ assert.eq(profileObj.keysExamined, 0, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 0, tojson(profileObj));
+
assert.eq(profileObj.protocol, getProfilerProtocolStringForCommand(conn), tojson(profileObj));
assert.eq(profileObj.command.aggregate, coll.getName(), tojson(profileObj));
assert(profileObj.hasOwnProperty("responseLength"), tojson(profileObj));
@@ -37,6 +42,21 @@
assert(profileObj.hasOwnProperty("locks"), tojson(profileObj));
//
+ // Confirm hasSortStage with in-memory sort.
+ //
+ coll.drop();
+ for (i = 0; i < 10; ++i) {
+ assert.writeOK(coll.insert({a: i}));
+ }
+
+ assert.eq(8, coll.aggregate([{$match: {a: {$gte: 2}}}, {$sort: {a: 1}}]).itcount());
+ profileObj = getLatestProfilerEntry(testDB);
+
+ // TODO SERVER-23265: hasSortStage should be present with a value of true.
+ // assert.eq(profileObj.hasSortStage, true, tojson(profileObj));
+ assert(!profileObj.hasOwnProperty("hasSortStage"), tojson(profileObj));
+
+ //
// Confirm "fromMultiPlanner" metric.
//
coll.drop();
diff --git a/jstests/core/profile_count.js b/jstests/core/profile_count.js
index 7e6d7b4834f..f5f58d7432e 100644
--- a/jstests/core/profile_count.js
+++ b/jstests/core/profile_count.js
@@ -1,5 +1,4 @@
// Confirms that profiled count execution contains all expected metrics with proper values.
-// TODO SERVER-23257: Add keysExamined, docsExamined.
// TODO SERVER-23259: Add planSummary.
// TODO SERVER-23264: Add execStats.
@@ -9,9 +8,9 @@
// For getLatestProfilerEntry and getProfilerProtocolStringForCommand
load("jstests/libs/profiler.js");
- var conn = new Mongo(db.getMongo().host);
- var testDB = conn.getDB("profile_count");
+ var testDB = db.getSiblingDB("profile_count");
assert.commandWorked(testDB.dropDatabase());
+ var conn = testDB.getMongo();
var coll = testDB.getCollection("test");
testDB.setProfilingLevel(2);
@@ -38,7 +37,7 @@
assert(profileObj.hasOwnProperty("locks"), tojson(profileObj));
//
- // Count with query.
+ // Count with non-indexed query.
//
coll.drop();
for (i = 0; i < 10; ++i) {
@@ -52,6 +51,25 @@
profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.command.query, query, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 10, tojson(profileObj));
+
+ //
+ // Count with indexed query.
+ //
+ coll.drop();
+ for (i = 0; i < 10; ++i) {
+ assert.writeOK(coll.insert({a: i}));
+ }
+ assert.commandWorked(coll.createIndex({a: 1}));
+
+ query = {
+ a: {$gte: 5}
+ };
+ assert.eq(5, coll.count(query));
+ profileObj = getLatestProfilerEntry(testDB);
+
+ assert.eq(profileObj.command.query, query, tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 6, tojson(profileObj));
//
// Confirm "fromMultiPlanner" metric.
@@ -67,4 +85,4 @@
profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.fromMultiPlanner, true, tojson(profileObj));
-})(); \ No newline at end of file
+})();
diff --git a/jstests/core/profile_delete.js b/jstests/core/profile_delete.js
index 8143242c6f6..0f89ff40093 100644
--- a/jstests/core/profile_delete.js
+++ b/jstests/core/profile_delete.js
@@ -1,5 +1,4 @@
// Confirms that profiled delete execution contains all expected metrics with proper values.
-// TODO SERVER-23257: Add keysExamined, docsExamined.
// TODO SERVER-23259: Add planSummary.
// TODO SERVER-23264: Add execStats.
@@ -20,16 +19,19 @@
//
var i;
for (i = 0; i < 10; ++i) {
- assert.writeOK(coll.insert({a: i}));
+ assert.writeOK(coll.insert({a: i, b: i}));
}
+ assert.commandWorked(coll.createIndex({a: 1}));
- assert.writeOK(coll.remove({a: {$gte: 2}}, {justOne: true}));
+ assert.writeOK(coll.remove({a: {$gte: 2}, b: {$gte: 2}}, {justOne: true}));
var profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "remove", tojson(profileObj));
assert.eq(profileObj.ndeleted, 1, tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 1, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 1, tojson(profileObj));
assert(profileObj.hasOwnProperty("millis"), tojson(profileObj));
assert(profileObj.hasOwnProperty("numYield"), tojson(profileObj));
assert(profileObj.hasOwnProperty("locks"), tojson(profileObj));
diff --git a/jstests/core/profile_distinct.js b/jstests/core/profile_distinct.js
index 3450bbe6d45..0c0b5723841 100644
--- a/jstests/core/profile_distinct.js
+++ b/jstests/core/profile_distinct.js
@@ -1,5 +1,4 @@
// Confirms that profiled distinct execution contains all expected metrics with proper values.
-// TODO SERVER-23257: Add keysExamined, docsExamined.
// TODO SERVER-23259: Add planSummary.
// TODO SERVER-23264: Add execStats.
@@ -9,9 +8,9 @@
// For getLatestProfilerEntry and getProfilerProtocolStringForCommand
load("jstests/libs/profiler.js");
- var conn = new Mongo(db.getMongo().host);
- var testDB = conn.getDB("profile_distinct");
+ var testDB = db.getSiblingDB("profile_distinct");
assert.commandWorked(testDB.dropDatabase());
+ var conn = testDB.getMongo();
var coll = testDB.getCollection("test");
testDB.setProfilingLevel(2);
@@ -21,14 +20,17 @@
//
var i;
for (i = 0; i < 10; ++i) {
- assert.writeOK(coll.insert({a: i % 5}));
+ assert.writeOK(coll.insert({a: i % 5, b: i}));
}
+ assert.commandWorked(coll.createIndex({b: 1}));
- coll.distinct("a", {a: {$gt: 2}});
+ coll.distinct("a", {b: {$gte: 5}});
var profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "command", tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 5, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 5, tojson(profileObj));
assert.eq(profileObj.protocol, getProfilerProtocolStringForCommand(conn), tojson(profileObj));
assert.eq(coll.getName(), profileObj.command.distinct, tojson(profileObj));
assert(profileObj.hasOwnProperty("responseLength"), tojson(profileObj));
diff --git a/jstests/core/profile_find.js b/jstests/core/profile_find.js
index c9e6af18a45..1368c0eb6e0 100644
--- a/jstests/core/profile_find.js
+++ b/jstests/core/profile_find.js
@@ -30,7 +30,6 @@
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "query", tojson(profileObj));
-
assert.eq(profileObj.keysExamined, 1, tojson(profileObj));
assert.eq(profileObj.docsExamined, 1, tojson(profileObj));
assert.eq(profileObj.nreturned, 1, tojson(profileObj));
diff --git a/jstests/core/profile_findandmodify.js b/jstests/core/profile_findandmodify.js
index 8a4d689894b..a0ac6df5329 100644
--- a/jstests/core/profile_findandmodify.js
+++ b/jstests/core/profile_findandmodify.js
@@ -1,5 +1,4 @@
// Confirms that profiled findAndModify execution contains all expected metrics with proper values.
-// TODO SERVER-23257: Add keysExamined, docsExamined.
// TODO SERVER-23259: Add planSummary.
// TODO SERVER-23264: Add execStats.
@@ -14,14 +13,14 @@
testDB.setProfilingLevel(2);
+ //
+ // Update as findAndModify.
+ //
coll.drop();
for (var i = 0; i < 3; i++) {
assert.writeOK(coll.insert({_id: i, a: i}));
}
- //
- // Update as findAndModify.
- //
assert.eq({_id: 2, a: 2}, coll.findAndModify({query: {a: 2}, update: {$inc: {b: 1}}}));
var profileObj = getLatestProfilerEntry(testDB);
@@ -41,29 +40,41 @@
//
// Delete as findAndModify.
//
- assert.eq({_id: 2, a: 2, b: 1}, coll.findAndModify({query: {a: 2}, remove: true}));
+ coll.drop();
+ for (var i = 0; i < 3; i++) {
+ assert.writeOK(coll.insert({_id: i, a: i}));
+ }
+
+ assert.eq({_id: 2, a: 2}, coll.findAndModify({query: {a: 2}, remove: true}));
profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.op, "command", tojson(profileObj));
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.command.query, {a: 2}, tojson(profileObj));
assert.eq(profileObj.command.remove, true, tojson(profileObj));
assert(!profileObj.hasOwnProperty("updateobj"), tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 0, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 3, tojson(profileObj));
assert.eq(profileObj.ndeleted, 1, tojson(profileObj));
//
// Update with {upsert: true} as findAndModify.
//
- assert.eq({_id: 2, a: 2, b: 1},
- coll.findAndModify(
- {query: {_id: 2, a: 2}, update: {$inc: {b: 1}}, upsert: true, new: true}));
+ coll.drop();
+ for (var i = 0; i < 3; i++) {
+ assert.writeOK(coll.insert({_id: i, a: i}));
+ }
+
+ assert.eq(
+ {_id: 4, a: 1},
+ coll.findAndModify({query: {_id: 4}, update: {$inc: {a: 1}}, upsert: true, new: true}));
profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.op, "command", tojson(profileObj));
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
- assert.eq(profileObj.command.query, {_id: 2, a: 2}, tojson(profileObj));
- assert.eq(profileObj.command.update, {$inc: {b: 1}}, tojson(profileObj));
+ assert.eq(profileObj.command.query, {_id: 4}, tojson(profileObj));
+ assert.eq(profileObj.command.update, {$inc: {a: 1}}, tojson(profileObj));
assert.eq(profileObj.command.upsert, true, tojson(profileObj));
assert.eq(profileObj.command.new, true, tojson(profileObj));
- assert.eq(profileObj.updateobj, {$inc: {b: 1}}, tojson(profileObj));
+ assert.eq(profileObj.updateobj, {$inc: {a: 1}}, tojson(profileObj));
assert.eq(profileObj.keysExamined, 0, tojson(profileObj));
assert.eq(profileObj.docsExamined, 0, tojson(profileObj));
assert.eq(profileObj.nMatched, 0, tojson(profileObj));
@@ -73,7 +84,12 @@
//
// Idhack update as findAndModify.
//
- assert.eq({_id: 2, a: 2, b: 1}, coll.findAndModify({query: {_id: 2}, update: {$inc: {b: 1}}}));
+ coll.drop();
+ for (var i = 0; i < 3; i++) {
+ assert.writeOK(coll.insert({_id: i, a: i}));
+ }
+
+ assert.eq({_id: 2, a: 2}, coll.findAndModify({query: {_id: 2}, update: {$inc: {b: 1}}}));
profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.keysExamined, 1, tojson(profileObj));
assert.eq(profileObj.docsExamined, 1, tojson(profileObj));
@@ -83,6 +99,11 @@
//
// Update as findAndModify with projection.
//
+ coll.drop();
+ for (var i = 0; i < 3; i++) {
+ assert.writeOK(coll.insert({_id: i, a: i}));
+ }
+
assert.eq({a: 2},
coll.findAndModify({query: {a: 2}, update: {$inc: {b: 1}}, fields: {_id: 0, a: 1}}));
profileObj = getLatestProfilerEntry(testDB);
@@ -100,6 +121,11 @@
//
// Delete as findAndModify with projection.
//
+ coll.drop();
+ for (var i = 0; i < 3; i++) {
+ assert.writeOK(coll.insert({_id: i, a: i}));
+ }
+
assert.eq({a: 2}, coll.findAndModify({query: {a: 2}, remove: true, fields: {_id: 0, a: 1}}));
profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.op, "command", tojson(profileObj));
@@ -111,6 +137,21 @@
assert.eq(profileObj.ndeleted, 1, tojson(profileObj));
//
+ // Confirm "hasSortStage" on findAndModify with sort.
+ //
+ coll.drop();
+ for (var i = 0; i < 3; i++) {
+ assert.writeOK(coll.insert({_id: i, a: i}));
+ }
+
+ assert.eq({_id: 0, a: 0},
+ coll.findAndModify({query: {a: {$gte: 0}}, sort: {a: 1}, update: {$inc: {b: 1}}}));
+
+ profileObj = getLatestProfilerEntry(testDB);
+
+ assert.eq(profileObj.hasSortStage, true, tojson(profileObj));
+
+ //
// Confirm "fromMultiPlanner" metric.
//
coll.drop();
diff --git a/jstests/core/profile_geonear.js b/jstests/core/profile_geonear.js
index a577723d788..c51306d4468 100644
--- a/jstests/core/profile_geonear.js
+++ b/jstests/core/profile_geonear.js
@@ -1,5 +1,4 @@
// Confirms that profiled geonear execution contains all expected metrics with proper values.
-// TODO SERVER-23257: Add keysExamined, docsExamined.
// TODO SERVER-23259: Add planSummary.
// TODO SERVER-23264: Add execStats.
@@ -9,9 +8,9 @@
// For getLatestProfilerEntry and getProfilerProtocolStringForCommand
load("jstests/libs/profiler.js");
- var conn = new Mongo(db.getMongo().host);
- var testDB = conn.getDB("profile_geonear");
+ var testDB = db.getSiblingDB("profile_geonear");
assert.commandWorked(testDB.dropDatabase());
+ var conn = testDB.getMongo();
var coll = testDB.getCollection("test");
testDB.setProfilingLevel(2);
@@ -32,6 +31,8 @@
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "command", tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 82, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 10, tojson(profileObj));
assert.eq(profileObj.protocol, getProfilerProtocolStringForCommand(conn), tojson(profileObj));
assert.eq(coll.getName(), profileObj.command.geoNear, tojson(profileObj));
assert(profileObj.hasOwnProperty("responseLength"), tojson(profileObj));
diff --git a/jstests/core/profile_getmore.js b/jstests/core/profile_getmore.js
index 8dd292d1b85..e6fc3edb66f 100644
--- a/jstests/core/profile_getmore.js
+++ b/jstests/core/profile_getmore.js
@@ -1,5 +1,4 @@
// Confirms that profiled getMore execution contains all expected metrics with proper values.
-// TODO SERVER-23257: Add keysExamined, docsExamined.
// TODO SERVER-23259: Add planSummary.
// TODO SERVER-23264: Add execStats.
@@ -35,6 +34,8 @@
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "getmore", tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 2, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 2, tojson(profileObj));
assert.eq(profileObj.cursorid, cursorId, tojson(profileObj));
assert.eq(profileObj.nreturned, 2, tojson(profileObj));
assert.eq(profileObj.query.batchSize, 2, tojson(profileObj));
@@ -48,17 +49,33 @@
assert(!profileObj.hasOwnProperty("cursorExhausted"), tojson(profileObj));
//
- // Confirm "cursorExhausted" metric
+ // Confirm hasSortStage on getMore with a not-exhausted cursor and in-memory sort.
+ //
+ coll.drop();
+ for (i = 0; i < 10; ++i) {
+ assert.writeOK(coll.insert({a: i}));
+ }
+
+ cursor = coll.find({a: {$gt: 0}}).sort({a: 1}).batchSize(2);
+ cursor.next(); // Perform initial query and consume first of 2 docs returned.
+ cursor.next(); // Consume second of 2 docs from initial query.
+ cursor.next(); // getMore performed, leaving open cursor.
+
+ profileObj = getLatestProfilerEntry(testDB);
+
+ assert.eq(profileObj.hasSortStage, true, tojson(profileObj));
+
+ //
+ // Confirm "cursorExhausted" metric.
//
coll.drop();
for (i = 0; i < 3; ++i) {
- assert.writeOK(coll.insert({a: i, b: i}));
+ assert.writeOK(coll.insert({a: i}));
}
- assert.commandWorked(coll.createIndex({a: 1}));
cursor = coll.find().batchSize(2);
cursor.next(); // Perform initial query and consume first of 3 docs returned.
- cursor.itcount(); // Exhaust the cursor
+ cursor.itcount(); // Exhaust the cursor.
profileObj = getLatestProfilerEntry(testDB);
diff --git a/jstests/core/profile_group.js b/jstests/core/profile_group.js
index 340b8f917f7..dce27d7e62c 100644
--- a/jstests/core/profile_group.js
+++ b/jstests/core/profile_group.js
@@ -1,5 +1,4 @@
// Confirms that profiled group execution contains all expected metrics with proper values.
-// TODO SERVER-23257: Add keysExamined, docsExamined.
// TODO SERVER-23259: Add planSummary.
// TODO SERVER-23264: Add execStats.
@@ -9,9 +8,9 @@
// For getLatestProfilerEntry and getProfilerProtocolStringForCommand
load("jstests/libs/profiler.js");
- var conn = new Mongo(db.getMongo().host);
- var testDB = conn.getDB("profile_group");
+ var testDB = db.getSiblingDB("profile_group");
assert.commandWorked(testDB.dropDatabase());
+ var conn = testDB.getMongo();
var coll = testDB.getCollection("test");
testDB.setProfilingLevel(2);
@@ -23,12 +22,15 @@
for (i = 0; i < 10; ++i) {
assert.writeOK(coll.insert({a: i, b: i % 5}));
}
+ assert.commandWorked(coll.createIndex({b: 1}));
coll.group({key: {a: 1, b: 1}, cond: {b: 3}, reduce: function() {}, initial: {}});
var profileObj = getLatestProfilerEntry(testDB);
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "command", tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 2, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 2, tojson(profileObj));
assert.eq(profileObj.protocol, getProfilerProtocolStringForCommand(conn), tojson(profileObj));
assert.eq(profileObj.command.group.key, {a: 1, b: 1}, tojson(profileObj));
assert(profileObj.hasOwnProperty("responseLength"), tojson(profileObj));
diff --git a/jstests/core/profile_mapreduce.js b/jstests/core/profile_mapreduce.js
index e43f7b687db..43ebdfea8f9 100644
--- a/jstests/core/profile_mapreduce.js
+++ b/jstests/core/profile_mapreduce.js
@@ -1,6 +1,5 @@
// Confirms that profiled findAndModify execution contains all expected metrics with proper values.
-// TODO SERVER-23261: Add keysExamined, docsExamined, fromMultiPlanner, replanned, hasSortStage,
-// and planSummary.
+// TODO SERVER-23261: Add planSummary.
(function() {
"use strict";
@@ -8,9 +7,9 @@
// For getLatestProfilerEntry and getProfilerProtocolStringForCommand
load("jstests/libs/profiler.js");
- var conn = new Mongo(db.getMongo().host);
- var testDB = conn.getDB("profile_mapreduce");
+ var testDB = db.getSiblingDB("profile_mapreduce");
assert.commandWorked(testDB.dropDatabase());
+ var conn = testDB.getMongo();
var coll = testDB.getCollection("test");
testDB.setProfilingLevel(2);
@@ -38,6 +37,8 @@
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "command", tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 3, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 3, tojson(profileObj));
assert.eq(profileObj.protocol, getProfilerProtocolStringForCommand(conn), tojson(profileObj));
assert.eq(coll.getName(), profileObj.command.mapreduce, tojson(profileObj));
assert(profileObj.hasOwnProperty("responseLength"), tojson(profileObj));
@@ -46,6 +47,19 @@
assert(profileObj.hasOwnProperty("locks"), tojson(profileObj));
//
+ // Confirm metrics for mapReduce with sort stage.
+ //
+ coll.drop();
+ for (var i = 0; i < 5; i++) {
+ assert.writeOK(coll.insert({a: i, b: i}));
+ }
+
+ coll.mapReduce(mapFunction, reduceFunction, {sort: {b: 1}, out: {inline: 1}});
+
+ profileObj = getLatestProfilerEntry(testDB);
+ assert.eq(profileObj.hasSortStage, true, tojson(profileObj));
+
+ //
// Confirm namespace field is correct when output is a collection.
//
coll.drop();
diff --git a/jstests/core/profile_update.js b/jstests/core/profile_update.js
index 0c8b5de00d5..ff34827dfb1 100644
--- a/jstests/core/profile_update.js
+++ b/jstests/core/profile_update.js
@@ -5,8 +5,6 @@
(function() {
"use strict";
- jsTest.log("Running profile_update under writeMode: " + db.getMongo().writeMode());
-
load("jstests/libs/profiler.js"); // For getLatestProfilerEntry.
// Setup test db and collection.
@@ -23,6 +21,7 @@
for (i = 0; i < 10; ++i) {
assert.writeOK(coll.insert({a: i}));
}
+ assert.commandWorked(coll.createIndex({a: 1}));
assert.writeOK(coll.update({a: {$gte: 2}}, {$set: {c: 1}}));
@@ -31,8 +30,8 @@
assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj));
assert.eq(profileObj.op, "update", tojson(profileObj));
assert.eq(profileObj.keyUpdates, 0, tojson(profileObj));
- assert.eq(profileObj.keysExamined, 0, tojson(profileObj));
- assert.eq(profileObj.docsExamined, 3, tojson(profileObj));
+ assert.eq(profileObj.keysExamined, 1, tojson(profileObj));
+ assert.eq(profileObj.docsExamined, 1, tojson(profileObj));
assert.eq(profileObj.nMatched, 1, tojson(profileObj));
assert.eq(profileObj.nModified, 1, tojson(profileObj));
assert(profileObj.hasOwnProperty("millis"), tojson(profileObj));
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index b8367c35642..6f4d183adee 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/exec/count.h"
#include "mongo/db/query/explain.h"
#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/range_preserver.h"
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/util/log.h"
@@ -165,8 +166,7 @@ public:
if (collection) {
collection->infoCache()->notifyOfQuery(txn, summaryStats.indexesUsed);
}
- CurOp::get(txn)->debug().fromMultiPlanner = summaryStats.fromMultiPlanner;
- CurOp::get(txn)->debug().replanned = summaryStats.replanned;
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(summaryStats);
// Plan is done executing. We just need to pull the count out of the root stage.
invariant(STAGE_COUNT == exec->getRootStage()->stageType());
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index 966a7d5b32b..16ea9564a64 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -48,6 +48,7 @@
#include "mongo/db/query/explain.h"
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/query/query_planner_common.h"
#include "mongo/util/log.h"
#include "mongo/util/timer.h"
@@ -234,8 +235,7 @@ public:
if (collection) {
collection->infoCache()->notifyOfQuery(txn, stats.indexesUsed);
}
- CurOp::get(txn)->debug().fromMultiPlanner = stats.fromMultiPlanner;
- CurOp::get(txn)->debug().replanned = stats.replanned;
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(stats);
verify(start == bb.buf());
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index c806e67bdb7..2bc45997561 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -59,6 +59,7 @@
#include "mongo/db/query/find_and_modify_request.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/plan_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/s/collection_sharding_state.h"
@@ -404,8 +405,7 @@ public:
if (collection) {
collection->infoCache()->notifyOfQuery(txn, summaryStats.indexesUsed);
}
- CurOp::get(txn)->debug().fromMultiPlanner = summaryStats.fromMultiPlanner;
- CurOp::get(txn)->debug().replanned = summaryStats.replanned;
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(summaryStats);
// Fill out OpDebug with the number of deleted docs.
CurOp::get(txn)->debug().ndeleted = getDeleteStats(exec.get())->docsDeleted;
@@ -494,7 +494,8 @@ public:
if (collection) {
collection->infoCache()->notifyOfQuery(txn, summaryStats.indexesUsed);
}
- UpdateStage::fillOutOpDebug(getUpdateStats(exec.get()), &summaryStats, opDebug);
+ UpdateStage::recordUpdateStatsInOpDebug(getUpdateStats(exec.get()), opDebug);
+ opDebug->setPlanSummaryMetrics(summaryStats);
boost::optional<BSONObj> value = advanceStatus.getValue();
appendCommandResponse(exec.get(), args.isRemove(), value, result);
diff --git a/src/mongo/db/commands/geo_near_cmd.cpp b/src/mongo/db/commands/geo_near_cmd.cpp
index 080903e2320..e431571b096 100644
--- a/src/mongo/db/commands/geo_near_cmd.cpp
+++ b/src/mongo/db/commands/geo_near_cmd.cpp
@@ -50,6 +50,7 @@
#include "mongo/db/query/explain.h"
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/range_preserver.h"
#include "mongo/platform/unordered_map.h"
#include "mongo/util/log.h"
@@ -280,15 +281,11 @@ public:
<< WorkingSetCommon::toStatusString(currObj)));
}
- // Fill out the stats subobj.
- BSONObjBuilder stats(result.subobjStart("stats"));
-
- // Fill in nscanned from the explain.
PlanSummaryStats summary;
Explain::getSummaryStats(*exec, &summary);
- collection->infoCache()->notifyOfQuery(txn, summary.indexesUsed);
- CurOp::get(txn)->debug().fromMultiPlanner = summary.fromMultiPlanner;
- CurOp::get(txn)->debug().replanned = summary.replanned;
+
+ // Fill out the stats subobj.
+ BSONObjBuilder stats(result.subobjStart("stats"));
stats.appendNumber("nscanned", summary.totalKeysExamined);
stats.appendNumber("objectsLoaded", summary.totalDocsExamined);
@@ -300,6 +297,10 @@ public:
stats.append("time", CurOp::get(txn)->elapsedMillis());
stats.done();
+ collection->infoCache()->notifyOfQuery(txn, summary.indexesUsed);
+
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(summary);
+
return true;
}
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp
index 9b7e54b04ca..5ba50fe0dde 100644
--- a/src/mongo/db/commands/getmore_cmd.cpp
+++ b/src/mongo/db/commands/getmore_cmd.cpp
@@ -48,6 +48,7 @@
#include "mongo/db/query/find.h"
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/getmore_request.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/s/operation_sharding_state.h"
@@ -320,6 +321,13 @@ public:
BSONObj obj;
PlanExecutor::ExecState state;
long long numResults = 0;
+
+ // We report keysExamined and docsExamined to OpDebug for a given getMore operation. To
+ // obtain these values we need to take a diff of the pre-execution and post-execution
+ // metrics, as they accumulate over the course of a cursor's lifetime.
+ PlanSummaryStats preExecutionStats;
+ Explain::getSummaryStats(*exec, &preExecutionStats);
+
Status batchStatus = generateBatch(cursor, request, &nextBatch, &state, &numResults);
if (!batchStatus.isOK()) {
return appendCommandStatus(result, batchStatus);
@@ -364,6 +372,12 @@ public:
}
}
+ PlanSummaryStats postExecutionStats;
+ Explain::getSummaryStats(*exec, &postExecutionStats);
+ postExecutionStats.totalKeysExamined -= preExecutionStats.totalKeysExamined;
+ postExecutionStats.totalDocsExamined -= preExecutionStats.totalDocsExamined;
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(postExecutionStats);
+
if (shouldSaveCursorGetMore(state, exec, isCursorTailable(cursor))) {
respondWithId = request.cursorid;
diff --git a/src/mongo/db/commands/group_cmd.cpp b/src/mongo/db/commands/group_cmd.cpp
index 25554261b69..60eeac1feb7 100644
--- a/src/mongo/db/commands/group_cmd.cpp
+++ b/src/mongo/db/commands/group_cmd.cpp
@@ -39,6 +39,7 @@
#include "mongo/db/exec/working_set_common.h"
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
namespace mongo {
@@ -172,8 +173,7 @@ private:
if (coll) {
coll->infoCache()->notifyOfQuery(txn, summaryStats.indexesUsed);
}
- CurOp::get(txn)->debug().fromMultiPlanner = summaryStats.fromMultiPlanner;
- CurOp::get(txn)->debug().replanned = summaryStats.replanned;
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(summaryStats);
invariant(STAGE_GROUP == planExecutor->getRootStage()->stageType());
GroupStage* groupStage = static_cast<GroupStage*>(planExecutor->getRootStage());
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index 1f97cce86da..8f125382095 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -58,6 +58,7 @@
#include "mongo/db/ops/insert.h"
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/range_preserver.h"
#include "mongo/db/repl/replication_coordinator_global.h"
@@ -1508,11 +1509,14 @@ public:
// Record the indexes used by the PlanExecutor.
PlanSummaryStats stats;
Explain::getSummaryStats(*exec, &stats);
+
+ // TODO SERVER-23261: Confirm whether this is the correct place to gather all
+ // metrics. There is no harm adding here for the time being.
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(stats);
+
Collection* coll = scopedAutoDb->getDb()->getCollection(config.ns);
invariant(coll); // 'exec' hasn't been killed, so collection must be alive.
coll->infoCache()->notifyOfQuery(txn, stats.indexesUsed);
- CurOp::get(txn)->debug().fromMultiPlanner = stats.fromMultiPlanner;
- CurOp::get(txn)->debug().replanned = stats.replanned;
}
pm.finished();
diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp
index d8836346c5e..ffe18cfb3cb 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -51,6 +51,7 @@
#include "mongo/db/query/cursor_response.h"
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/storage/storage_options.h"
#include "mongo/stdx/memory.h"
#include "mongo/util/scopeguard.h"
@@ -247,8 +248,10 @@ public:
PlanSummaryStats stats;
Explain::getSummaryStats(*input, &stats);
collection->infoCache()->notifyOfQuery(txn, stats.indexesUsed);
- CurOp::get(txn)->debug().fromMultiPlanner = stats.fromMultiPlanner;
- CurOp::get(txn)->debug().replanned = stats.replanned;
+
+ // TODO SERVER-23265: Confirm whether this is the correct place to gather all
+ // metrics. There is no harm adding here for the time being.
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(stats);
}
// Create the PlanExecutor which returns results from the pipeline. The WorkingSet
diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp
index bd71a02d6aa..e20696f3394 100644
--- a/src/mongo/db/commands/write_commands/batch_executor.cpp
+++ b/src/mongo/db/commands/write_commands/batch_executor.cpp
@@ -58,6 +58,7 @@
#include "mongo/db/ops/update_lifecycle_impl.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/plan_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/query/query_knobs.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/repl_client_info.h"
@@ -1126,7 +1127,8 @@ static void multiUpdate(OperationContext* txn,
collection->infoCache()->notifyOfQuery(txn, summary.indexesUsed);
const UpdateStats* updateStats = UpdateStage::getUpdateStats(exec.get());
- UpdateStage::fillOutOpDebug(updateStats, &summary, debug);
+ UpdateStage::recordUpdateStatsInOpDebug(updateStats, debug);
+ debug->setPlanSummaryMetrics(summary);
UpdateResult res = UpdateStage::makeUpdateResult(updateStats);
@@ -1248,8 +1250,7 @@ static void multiRemove(OperationContext* txn,
if (collection) {
collection->infoCache()->notifyOfQuery(txn, summary.indexesUsed);
}
- CurOp::get(txn)->debug().fromMultiPlanner = summary.fromMultiPlanner;
- CurOp::get(txn)->debug().replanned = summary.replanned;
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(summary);
if (repl::ReplClientInfo::forClient(client).getLastOp() != lastOpAtOperationStart) {
// If this operation has already generated a new lastOp, don't bother setting it
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index dc9f819dc73..6b8618b3843 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -40,6 +40,7 @@
#include "mongo/db/cursor_id.h"
#include "mongo/db/json.h"
#include "mongo/db/query/getmore_request.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
@@ -674,4 +675,13 @@ void OpDebug::append(const CurOp& curop,
execStats.append(b, "execStats");
}
+void OpDebug::setPlanSummaryMetrics(const PlanSummaryStats& planSummaryStats) {
+ keysExamined = planSummaryStats.totalKeysExamined;
+ docsExamined = planSummaryStats.totalDocsExamined;
+ idhack = planSummaryStats.isIdhack;
+ hasSortStage = planSummaryStats.hasSortStage;
+ fromMultiPlanner = planSummaryStats.fromMultiPlanner;
+ replanned = planSummaryStats.replanned;
+}
+
} // namespace mongo
diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h
index 2b79b888e0e..06539aeaacd 100644
--- a/src/mongo/db/curop.h
+++ b/src/mongo/db/curop.h
@@ -47,6 +47,7 @@ class Client;
class Command;
class CurOp;
class OperationContext;
+struct PlanSummaryStats;
/**
* stores a copy of a bson obj in a fixed size buffer
@@ -141,6 +142,11 @@ public:
const SingleThreadedLockStats& lockStats,
BSONObjBuilder& builder) const;
+ /**
+ * Copies relevant plan summary metrics to this OpDebug instance.
+ */
+ void setPlanSummaryMetrics(const PlanSummaryStats& planSummaryStats);
+
// -------------------
// basic options
diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp
index d6118a1187e..1b52796a5a5 100644
--- a/src/mongo/db/exec/update.cpp
+++ b/src/mongo/db/exec/update.cpp
@@ -1031,20 +1031,12 @@ const UpdateStats* UpdateStage::getUpdateStats(const PlanExecutor* exec) {
return static_cast<const UpdateStats*>(updateStage->getSpecificStats());
}
-void UpdateStage::fillOutOpDebug(const UpdateStats* updateStats,
- const PlanSummaryStats* summaryStats,
- OpDebug* opDebug) {
+void UpdateStage::recordUpdateStatsInOpDebug(const UpdateStats* updateStats, OpDebug* opDebug) {
opDebug->nMatched = updateStats->nMatched;
opDebug->nModified = updateStats->nModified;
opDebug->upsert = updateStats->inserted;
opDebug->fastmodinsert = updateStats->fastmodinsert;
opDebug->fastmod = updateStats->fastmod;
-
- // Copy summary information about the plan into OpDebug.
- opDebug->keysExamined = summaryStats->totalKeysExamined;
- opDebug->docsExamined = summaryStats->totalDocsExamined;
- opDebug->fromMultiPlanner = summaryStats->fromMultiPlanner;
- opDebug->replanned = summaryStats->replanned;
}
UpdateResult UpdateStage::makeUpdateResult(const UpdateStats* updateStats) {
diff --git a/src/mongo/db/exec/update.h b/src/mongo/db/exec/update.h
index 0fa391c778f..c39717f9748 100644
--- a/src/mongo/db/exec/update.h
+++ b/src/mongo/db/exec/update.h
@@ -106,12 +106,9 @@ public:
static const UpdateStats* getUpdateStats(const PlanExecutor* exec);
/**
- * Populate 'opDebug' with stats from 'updateStats' and 'summaryStats' describing the execution
- * of this update.
+ * Populate 'opDebug' with stats from 'updateStats' describing the execution of this update.
*/
- static void fillOutOpDebug(const UpdateStats* updateStats,
- const PlanSummaryStats* summaryStats,
- OpDebug* opDebug);
+ static void recordUpdateStatsInOpDebug(const UpdateStats* updateStats, OpDebug* opDebug);
/**
* Converts 'updateStats' into an UpdateResult.
diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp
index 947f0485c2c..bce793e9c6c 100644
--- a/src/mongo/db/instance.cpp
+++ b/src/mongo/db/instance.cpp
@@ -76,6 +76,7 @@
#include "mongo/db/ops/update_request.h"
#include "mongo/db/query/find.h"
#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator_global.h"
@@ -730,7 +731,8 @@ void receivedUpdate(OperationContext* txn, const NamespaceString& nsString, Mess
}
const UpdateStats* updateStats = UpdateStage::getUpdateStats(exec.get());
- UpdateStage::fillOutOpDebug(updateStats, &summary, &op.debug());
+ UpdateStage::recordUpdateStatsInOpDebug(updateStats, &op.debug());
+ op.debug().setPlanSummaryMetrics(summary);
UpdateResult res = UpdateStage::makeUpdateResult(updateStats);
@@ -794,7 +796,8 @@ void receivedUpdate(OperationContext* txn, const NamespaceString& nsString, Mess
collection->infoCache()->notifyOfQuery(txn, summary.indexesUsed);
const UpdateStats* updateStats = UpdateStage::getUpdateStats(exec.get());
- UpdateStage::fillOutOpDebug(updateStats, &summary, &op.debug());
+ UpdateStage::recordUpdateStatsInOpDebug(updateStats, &op.debug());
+ op.debug().setPlanSummaryMetrics(summary);
UpdateResult res = UpdateStage::makeUpdateResult(updateStats);
@@ -875,8 +878,7 @@ void receivedDelete(OperationContext* txn, const NamespaceString& nsString, Mess
if (collection) {
collection->infoCache()->notifyOfQuery(txn, summary.indexesUsed);
}
- op.debug().fromMultiPlanner = summary.fromMultiPlanner;
- op.debug().replanned = summary.replanned;
+ CurOp::get(txn)->debug().setPlanSummaryMetrics(summary);
if (repl::ReplClientInfo::forClient(client).getLastOp() != lastOpAtOperationStart) {
// If this operation has already generated a new lastOp, don't bother setting it
diff --git a/src/mongo/db/ops/update.cpp b/src/mongo/db/ops/update.cpp
index 94caffaed71..010abf6be3b 100644
--- a/src/mongo/db/ops/update.cpp
+++ b/src/mongo/db/ops/update.cpp
@@ -48,6 +48,7 @@
#include "mongo/db/ops/update_lifecycle.h"
#include "mongo/db/query/explain.h"
#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/update_index_data.h"
@@ -126,7 +127,8 @@ UpdateResult update(OperationContext* txn,
PlanSummaryStats summaryStats;
Explain::getSummaryStats(*exec, &summaryStats);
const UpdateStats* updateStats = UpdateStage::getUpdateStats(exec.get());
- UpdateStage::fillOutOpDebug(updateStats, &summaryStats, opDebug);
+ UpdateStage::recordUpdateStatsInOpDebug(updateStats, opDebug);
+ opDebug->setPlanSummaryMetrics(summaryStats);
return UpdateStage::makeUpdateResult(updateStats);
}
diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp
index 890d50e9021..a1eb04332e3 100644
--- a/src/mongo/db/query/explain.cpp
+++ b/src/mongo/db/query/explain.cpp
@@ -41,6 +41,7 @@
#include "mongo/db/exec/text.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/plan_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/query/query_settings.h"
#include "mongo/db/query/stage_builder.h"
diff --git a/src/mongo/db/query/explain.h b/src/mongo/db/query/explain.h
index 21edf161597..95fc193cbb2 100644
--- a/src/mongo/db/query/explain.h
+++ b/src/mongo/db/query/explain.h
@@ -40,40 +40,7 @@ namespace mongo {
class Collection;
class OperationContext;
-
-/**
- * A container for the summary statistics that the profiler, slow query log, and
- * other non-explain debug mechanisms may want to collect.
- */
-struct PlanSummaryStats {
- // The number of results returned by the plan.
- size_t nReturned = 0U;
-
- // The total number of index keys examined by the plan.
- size_t totalKeysExamined = 0U;
-
- // The total number of documents examined by the plan.
- size_t totalDocsExamined = 0U;
-
- // The number of milliseconds spent inside the root stage's work() method.
- long long executionTimeMillis = 0;
-
- // Did this plan use the fast path for key-value retrievals on the _id index?
- bool isIdhack = false;
-
- // Did this plan use an in-memory sort stage?
- bool hasSortStage = false;
-
- // The names of each index used by the plan.
- std::set<std::string> indexesUsed;
-
- // Was this plan a result of using the MultiPlanStage to select a winner among several
- // candidates?
- bool fromMultiPlanner = false;
-
- // Was a replan triggered during the execution of this query?
- bool replanned = false;
-};
+struct PlanSummaryStats;
/**
* Namespace for the collection of static methods used to generate explain information.
diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp
index edc77d515bf..c54be93d0e5 100644
--- a/src/mongo/db/query/find.cpp
+++ b/src/mongo/db/query/find.cpp
@@ -49,6 +49,7 @@
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/internal_plans.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/s/collection_sharding_state.h"
@@ -153,12 +154,7 @@ void endQueryOp(OperationContext* txn,
// Fill out curop based on explain summary statistics.
PlanSummaryStats summaryStats;
Explain::getSummaryStats(exec, &summaryStats);
- curop->debug().hasSortStage = summaryStats.hasSortStage;
- curop->debug().keysExamined = summaryStats.totalKeysExamined;
- curop->debug().docsExamined = summaryStats.totalDocsExamined;
- curop->debug().idhack = summaryStats.isIdhack;
- curop->debug().fromMultiPlanner = summaryStats.fromMultiPlanner;
- curop->debug().replanned = summaryStats.replanned;
+ curop->debug().setPlanSummaryMetrics(summaryStats);
if (collection) {
collection->infoCache()->notifyOfQuery(txn, summaryStats.indexesUsed);
@@ -393,6 +389,12 @@ QueryResult::View getMore(OperationContext* txn,
exec->restoreState();
PlanExecutor::ExecState state;
+ // We report keysExamined and docsExamined to OpDebug for a given getMore operation. To
+ // obtain these values we need to take a diff of the pre-execution and post-execution
+ // metrics, as they accumulate over the course of a cursor's lifetime.
+ PlanSummaryStats preExecutionStats;
+ Explain::getSummaryStats(*exec, &preExecutionStats);
+
generateBatch(ntoreturn, cc, &bb, &numResults, &slaveReadTill, &state);
// If this is an await data cursor, and we hit EOF without generating any results, then
@@ -420,6 +422,12 @@ QueryResult::View getMore(OperationContext* txn,
generateBatch(ntoreturn, cc, &bb, &numResults, &slaveReadTill, &state);
}
+ PlanSummaryStats postExecutionStats;
+ Explain::getSummaryStats(*exec, &postExecutionStats);
+ postExecutionStats.totalKeysExamined -= preExecutionStats.totalKeysExamined;
+ postExecutionStats.totalDocsExamined -= preExecutionStats.totalDocsExamined;
+ curop.debug().setPlanSummaryMetrics(postExecutionStats);
+
// We have to do this before re-acquiring locks in the agg case because
// shouldSaveCursorGetMore() can make a network call for agg cursors.
//
diff --git a/src/mongo/db/query/plan_summary_stats.h b/src/mongo/db/query/plan_summary_stats.h
new file mode 100644
index 00000000000..8168ea9f401
--- /dev/null
+++ b/src/mongo/db/query/plan_summary_stats.h
@@ -0,0 +1,68 @@
+/* Copyright 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace mongo {
+
+/**
+ * A container for the summary statistics that the profiler, slow query log, and
+ * other non-explain debug mechanisms may want to collect.
+ */
+struct PlanSummaryStats {
+ // The number of results returned by the plan.
+ size_t nReturned = 0U;
+
+ // The total number of index keys examined by the plan.
+ size_t totalKeysExamined = 0U;
+
+ // The total number of documents examined by the plan.
+ size_t totalDocsExamined = 0U;
+
+ // The number of milliseconds spent inside the root stage's work() method.
+ long long executionTimeMillis = 0;
+
+ // Did this plan use the fast path for key-value retrievals on the _id index?
+ bool isIdhack = false;
+
+ // Did this plan use an in-memory sort stage?
+ bool hasSortStage = false;
+
+ // The names of each index used by the plan.
+ std::set<std::string> indexesUsed;
+
+ // Was this plan a result of using the MultiPlanStage to select a winner among several
+ // candidates?
+ bool fromMultiPlanner = false;
+
+ // Was a replan triggered during the execution of this query?
+ bool replanned = false;
+};
+
+} // namespace mongo
diff --git a/src/mongo/dbtests/query_stage_multiplan.cpp b/src/mongo/dbtests/query_stage_multiplan.cpp
index a26a657d4b5..4056c439545 100644
--- a/src/mongo/dbtests/query_stage_multiplan.cpp
+++ b/src/mongo/dbtests/query_stage_multiplan.cpp
@@ -44,6 +44,7 @@
#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/plan_executor.h"
+#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/db/query/query_knobs.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/query/query_planner_test_lib.h"