summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2014-10-13 16:01:05 -0400
committerDavid Storch <david.storch@10gen.com>2014-10-14 15:56:55 -0400
commita0b44fd4cfdbd2b40f99317b4aceb8b58ab5e3ce (patch)
treea59e4168515fbb1491c4577ab34bec1a11894944
parentf919a33b0d8ca3767e40c0cf9588f1a8d25b7cee (diff)
downloadmongo-a0b44fd4cfdbd2b40f99317b4aceb8b58ab5e3ce.tar.gz
SERVER-15527 change rejectedPlansExecution to allPlansExecution
-rw-r--r--jstests/core/explain_shell_helpers.js12
-rw-r--r--src/mongo/db/exec/multi_plan.cpp54
-rw-r--r--src/mongo/db/exec/multi_plan.h15
-rw-r--r--src/mongo/db/query/explain.cpp53
-rw-r--r--src/mongo/s/cluster_explain.cpp34
5 files changed, 63 insertions, 105 deletions
diff --git a/jstests/core/explain_shell_helpers.js b/jstests/core/explain_shell_helpers.js
index 5cc0a4bbfd3..785dc926b99 100644
--- a/jstests/core/explain_shell_helpers.js
+++ b/jstests/core/explain_shell_helpers.js
@@ -35,14 +35,14 @@ explain = t.explain(true).find().finish();
assert.commandWorked(explain);
assert("queryPlanner" in explain);
assert("executionStats" in explain);
-assert("rejectedPlansExecution" in explain.executionStats);
+assert("allPlansExecution" in explain.executionStats);
// .explain(true) after .find().
explain = t.find().explain(true);
assert.commandWorked(explain);
assert("queryPlanner" in explain);
assert("executionStats" in explain);
-assert("rejectedPlansExecution" in explain.executionStats);
+assert("allPlansExecution" in explain.executionStats);
//
// Test verbosity specifiers.
@@ -63,24 +63,24 @@ explain = t.explain("executionStats").find().finish();
assert.commandWorked(explain);
assert("queryPlanner" in explain);
assert("executionStats" in explain);
-assert(!("rejectedPlansExecution" in explain.executionStats));
+assert(!("allPlansExecution" in explain.executionStats));
explain = t.find().explain("executionStats");
assert.commandWorked(explain);
assert("queryPlanner" in explain);
assert("executionStats" in explain);
-assert(!("rejectedPlansExecution" in explain.executionStats));
+assert(!("allPlansExecution" in explain.executionStats));
// "allPlansExecution"
explain = t.explain("allPlansExecution").find().finish();
assert.commandWorked(explain);
assert("queryPlanner" in explain);
assert("executionStats" in explain);
-assert("rejectedPlansExecution" in explain.executionStats);
+assert("allPlansExecution" in explain.executionStats);
explain = t.find().explain("allPlansExecution");
assert.commandWorked(explain);
assert("queryPlanner" in explain);
assert("executionStats" in explain);
-assert("rejectedPlansExecution" in explain.executionStats);
+assert("allPlansExecution" in explain.executionStats);
//
// Tests for DBExplainQuery helpers.
diff --git a/src/mongo/db/exec/multi_plan.cpp b/src/mongo/db/exec/multi_plan.cpp
index 2732c501bba..3db8e3e4dd7 100644
--- a/src/mongo/db/exec/multi_plan.cpp
+++ b/src/mongo/db/exec/multi_plan.cpp
@@ -30,14 +30,15 @@
#include "mongo/platform/basic.h"
+#include <algorithm>
+#include <math.h>
+
+#include "mongo/base/owned_pointer_vector.h"
#include "mongo/db/exec/multi_plan.h"
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/working_set_common.h"
#include "mongo/util/mongoutils/str.h"
-#include <algorithm>
-#include <math.h>
-
// for updateCache
#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/database.h"
@@ -71,12 +72,6 @@ namespace mongo {
delete _candidates[ix].solution;
delete _candidates[ix].root;
}
-
- for (vector<PlanStageStats*>::iterator it = _candidateStats.begin();
- it != _candidateStats.end();
- ++it) {
- delete *it;
- }
}
void MultiPlanStage::addPlan(QuerySolution* solution, PlanStage* root,
@@ -298,20 +293,19 @@ namespace mongo {
}
vector<PlanStageStats*> MultiPlanStage::generateCandidateStats() {
+ OwnedPointerVector<PlanStageStats> candidateStats;
+
for (size_t ix = 0; ix < _candidates.size(); ix++) {
if (ix == (size_t)_bestPlanIdx) { continue; }
if (ix == (size_t)_backupPlanIdx) { continue; }
- // Remember the stats for the candidate plan because we always show it on an
- // explain. (The {verbose:false} in explain() is client-side trick; we always
- // generate a "verbose" explain.)
PlanStageStats* stats = _candidates[ix].root->getStats();
if (stats) {
- _candidateStats.push_back(stats);
+ candidateStats.push_back(stats);
}
}
- return _candidateStats;
+ return candidateStats.release();
}
bool MultiPlanStage::workAllPlans(size_t numResults) {
@@ -362,38 +356,6 @@ namespace mongo {
return !doneWorking;
}
- Status MultiPlanStage::executeAllPlans() {
- // Boolean vector keeping track of which plans are done.
- vector<bool> planDone(_candidates.size(), false);
-
- // Number of plans that are done.
- size_t doneCount = 0;
-
- while (doneCount < _candidates.size()) {
- for (size_t i = 0; i < _candidates.size(); i++) {
- if (planDone[i]) {
- continue;
- }
-
- WorkingSetID id = WorkingSet::INVALID_ID;
- PlanStage::StageState state = _candidates[i].root->work(&id);
-
- if (PlanStage::IS_EOF == state || PlanStage::DEAD == state) {
- doneCount++;
- planDone[i] = true;
- }
- else if (PlanStage::FAILURE == state) {
- // Propogate error.
- BSONObj errObj;
- WorkingSetCommon::getStatusMemberObject(*_candidates[i].ws, id, &errObj);
- return Status(ErrorCodes::BadValue, WorkingSetCommon::toStatusString(errObj));
- }
- }
- }
-
- return Status::OK();
- }
-
void MultiPlanStage::saveState() {
for (size_t i = 0; i < _candidates.size(); ++i) {
_candidates[i].root->saveState();
diff --git a/src/mongo/db/exec/multi_plan.h b/src/mongo/db/exec/multi_plan.h
index 689665e81f6..964d71443d0 100644
--- a/src/mongo/db/exec/multi_plan.h
+++ b/src/mongo/db/exec/multi_plan.h
@@ -111,19 +111,11 @@ namespace mongo {
//
/**
- * Gathers execution stats for all losing plans.
+ * Gathers execution stats for all losing plans. Caller takes ownership of
+ * all pointers in the returned vector.
*/
vector<PlanStageStats*> generateCandidateStats();
- /**
- * Runs the candidate plans until each has either hit EOF or returned DEAD. Results
- * from the plans are thrown out, but execution stats are gathered.
- *
- * You should call this after calling pickBestPlan(...). It expects that a winning plan
- * has already been selected.
- */
- Status executeAllPlans();
-
static const char* kStageType;
private:
@@ -156,9 +148,6 @@ namespace mongo {
// tranferred to the PlanExecutor that wraps this stage.
std::vector<CandidatePlan> _candidates;
- // Candidate plans' stats. Owned here.
- std::vector<PlanStageStats*> _candidateStats;
-
// index into _candidates, of the winner of the plan competition
// uses -1 / kNoSuchPlan when best plan is not (yet) known
int _bestPlanIdx;
diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp
index 234f86b5f30..83574eae575 100644
--- a/src/mongo/db/query/explain.cpp
+++ b/src/mongo/db/query/explain.cpp
@@ -30,6 +30,7 @@
#include "mongo/db/query/explain.h"
+#include "mongo/base/owned_pointer_vector.h"
#include "mongo/db/exec/multi_plan.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/plan_executor.h"
@@ -561,20 +562,17 @@ namespace mongo {
// Inspect the tree to see if there is a MultiPlanStage.
MultiPlanStage* mps = getMultiPlanStage(exec->getRootStage());
- // The executionStats verbosity level requires that we run the winning plan
- // until it finishes.
- if (verbosity >= ExplainCommon::EXEC_STATS) {
- Status s = exec->executePlan();
- if (!s.isOK()) {
- return s;
- }
+ // Get stats of the winning plan from the trial period, if the verbosity level
+ // is high enough and there was a runoff between multiple plans.
+ auto_ptr<PlanStageStats> winningStatsTrial;
+ if (verbosity >= ExplainCommon::EXEC_ALL_PLANS && NULL != mps) {
+ winningStatsTrial.reset(exec->getStats());
+ invariant(winningStatsTrial.get());
}
- // The allPlansExecution verbosity level requires that we run all plans to completion,
- // if there are multiple candidates. If 'mps' is NULL, then there was only one candidate,
- // and we don't have to worry about gathering stats for rejected plans.
- if (verbosity == ExplainCommon::EXEC_ALL_PLANS && NULL != mps) {
- Status s = mps->executeAllPlans();
+ // If we need execution stats, then run the plan in order to gather the stats.
+ if (verbosity >= ExplainCommon::EXEC_STATS) {
+ Status s = exec->executePlan();
if (!s.isOK()) {
return s;
}
@@ -587,10 +585,10 @@ namespace mongo {
// Get stats for the winning plan.
scoped_ptr<PlanStageStats> winningStats(exec->getStats());
- // Get stats for the rejected plans, if there were rehected plans.
- vector<PlanStageStats*> rejectedStats;
+ // Get stats for the rejected plans, if more than one plan was considered.
+ OwnedPointerVector<PlanStageStats> allPlansStats;
if (NULL != mps) {
- rejectedStats = mps->generateCandidateStats();
+ allPlansStats = mps->generateCandidateStats();
}
//
@@ -599,7 +597,7 @@ namespace mongo {
CanonicalQuery* query = exec->getCanonicalQuery();
if (verbosity >= ExplainCommon::QUERY_PLANNER) {
- generatePlannerInfo(query, winningStats.get(), rejectedStats, out);
+ generatePlannerInfo(query, winningStats.get(), allPlansStats.vector(), out);
}
if (verbosity >= ExplainCommon::EXEC_STATS) {
@@ -609,16 +607,25 @@ namespace mongo {
long long totalTimeMillis = opCtx->getCurOp()->elapsedMillis();
generateExecStats(winningStats.get(), verbosity, &execBob, totalTimeMillis);
- // Also generate exec stats for each rejected plan, if the verbosity level
- // is high enough.
+ // Also generate exec stats for all plans, if the verbosity level is high enough.
+ // These stats reflect what happened during the trial period that ranked the plans.
if (verbosity >= ExplainCommon::EXEC_ALL_PLANS) {
- BSONArrayBuilder rejectedBob(execBob.subarrayStart("rejectedPlansExecution"));
- for (size_t i = 0; i < rejectedStats.size(); ++i) {
- BSONObjBuilder planBob(rejectedBob.subobjStart());
- generateExecStats(rejectedStats[i], verbosity, &planBob);
+ // If we ranked multiple plans against each other, then add stats collected
+ // from the trial period of the winning plan. The "allPlansExecution" section
+ // will contain an apples-to-apples comparison of the winning plan's stats against
+ // all rejected plans' stats collected during the trial period.
+ if (NULL != mps) {
+ invariant(winningStatsTrial.get());
+ allPlansStats.push_back(winningStatsTrial.release());
+ }
+
+ BSONArrayBuilder allPlansBob(execBob.subarrayStart("allPlansExecution"));
+ for (size_t i = 0; i < allPlansStats.size(); ++i) {
+ BSONObjBuilder planBob(allPlansBob.subobjStart());
+ generateExecStats(allPlansStats[i], verbosity, &planBob);
planBob.doneFast();
}
- rejectedBob.doneFast();
+ allPlansBob.doneFast();
}
execBob.doneFast();
diff --git a/src/mongo/s/cluster_explain.cpp b/src/mongo/s/cluster_explain.cpp
index 6e0a928f85d..a90d4baa9a2 100644
--- a/src/mongo/s/cluster_explain.cpp
+++ b/src/mongo/s/cluster_explain.cpp
@@ -122,10 +122,10 @@ namespace mongo {
return Status(ErrorCodes::InternalError, "no shards found for explain");
}
- // Count up the number of shards that have execution stats and rejected plans
+ // Count up the number of shards that have execution stats and all plans
// execution level information.
size_t numShardsExecStats = 0;
- size_t numShardsRejectedExecStats = 0;
+ size_t numShardsAllPlansStats = 0;
// Check that the result from each shard has a true value for "ok" and has
// the expected "queryPlanner" field.
@@ -140,8 +140,8 @@ namespace mongo {
if (shardResults[i].result.hasField("executionStats")) {
numShardsExecStats++;
BSONObj execStats = shardResults[i].result["executionStats"].Obj();
- if (execStats.hasField("rejectedPlansExecution")) {
- numShardsRejectedExecStats++;
+ if (execStats.hasField("allPlansExecution")) {
+ numShardsAllPlansStats++;
}
}
}
@@ -154,10 +154,10 @@ namespace mongo {
<< " had executionStats explain information.");
}
- // Either all shards should have rejected plans execution stats, or none should.
- if (0 != numShardsRejectedExecStats && shardResults.size() != numShardsRejectedExecStats) {
+ // Either all shards should have all plans execution stats, or none should.
+ if (0 != numShardsAllPlansStats && shardResults.size() != numShardsAllPlansStats) {
return Status(ErrorCodes::InternalError,
- str::stream() << "Only " << numShardsRejectedExecStats
+ str::stream() << "Only " << numShardsAllPlansStats
<< "/" << shardResults.size()
<< " had allPlansExecution explain information.");
}
@@ -278,32 +278,32 @@ namespace mongo {
executionStagesBob.doneFast();
- if (!shardResults[0].result["executionStats"].Obj().hasField("rejectedPlansExecution")) {
- // The shards don't have execution stats for rejected plans, so we're done.
+ if (!shardResults[0].result["executionStats"].Obj().hasField("allPlansExecution")) {
+ // The shards don't have execution stats for all plans, so we're done.
executionStatsBob.doneFast();
return;
}
- // Add the rejected plans from each shard.
- BSONArrayBuilder rejectedPlansExecBob(
- executionStatsBob.subarrayStart("rejectedPlansExecution"));
+ // Add the allPlans stats from each shard.
+ BSONArrayBuilder allPlansExecBob(
+ executionStatsBob.subarrayStart("allPlansExecution"));
for (size_t i = 0; i < shardResults.size(); i++) {
BSONObjBuilder singleShardBob(execShardsBuilder.subobjStart());
singleShardBob.append("shardName", shardResults[i].shardTarget.getName());
BSONObj execStats = shardResults[i].result["executionStats"].Obj();
- vector<BSONElement> rejectedPlans = execStats["rejectedPlansExecution"].Array();
+ vector<BSONElement> allPlans = execStats["allPlansExecution"].Array();
- BSONArrayBuilder innerArrayBob(singleShardBob.subarrayStart("rejectedPlans"));
- for (size_t j = 0; j < rejectedPlans.size(); j++) {
- appendToArrayIfRoom(&innerArrayBob, rejectedPlans[j]);
+ BSONArrayBuilder innerArrayBob(singleShardBob.subarrayStart("allPlans"));
+ for (size_t j = 0; j < allPlans.size(); j++) {
+ appendToArrayIfRoom(&innerArrayBob, allPlans[j]);
}
innerArrayBob.done();
singleShardBob.doneFast();
}
- rejectedPlansExecBob.doneFast();
+ allPlansExecBob.doneFast();
executionStatsBob.doneFast();
}