diff options
author | David Storch <david.storch@10gen.com> | 2014-10-13 16:01:05 -0400 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2014-10-14 15:56:55 -0400 |
commit | a0b44fd4cfdbd2b40f99317b4aceb8b58ab5e3ce (patch) | |
tree | a59e4168515fbb1491c4577ab34bec1a11894944 | |
parent | f919a33b0d8ca3767e40c0cf9588f1a8d25b7cee (diff) | |
download | mongo-a0b44fd4cfdbd2b40f99317b4aceb8b58ab5e3ce.tar.gz |
SERVER-15527 change rejectedPlansExecution to allPlansExecution
-rw-r--r-- | jstests/core/explain_shell_helpers.js | 12 | ||||
-rw-r--r-- | src/mongo/db/exec/multi_plan.cpp | 54 | ||||
-rw-r--r-- | src/mongo/db/exec/multi_plan.h | 15 | ||||
-rw-r--r-- | src/mongo/db/query/explain.cpp | 53 | ||||
-rw-r--r-- | src/mongo/s/cluster_explain.cpp | 34 |
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(); } |