diff options
author | Charlie Swanson <charlie.swanson@mongodb.com> | 2015-10-08 13:15:59 -0400 |
---|---|---|
committer | Charlie Swanson <charlie.swanson@mongodb.com> | 2015-10-08 16:56:37 -0400 |
commit | 5aefcdd1e32f411c35c4c3add6e67dcd9ccbee54 (patch) | |
tree | 07ef20f49abc088f3c147b63ce49d4e17947699e /src | |
parent | 4a42d61c6463e774c2308ae2f329e168759aa390 (diff) | |
download | mongo-5aefcdd1e32f411c35c4c3add6e67dcd9ccbee54.tar.gz |
SERVER-20111 Plan summary should only include the winning plan
Diffstat (limited to 'src')
30 files changed, 190 insertions, 86 deletions
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index 78b367a39ea..79d2f6405b2 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -73,7 +73,7 @@ const UpdateStats* getUpdateStats(const PlanStageStats* stats) { // The stats may refer to an update stage, or a projection stage wrapping an update stage. if (StageType::STAGE_PROJECTION == stats->stageType) { invariant(stats->children.size() == 1); - stats = stats->children[0]; + stats = stats->children[0].get(); } invariant(StageType::STAGE_UPDATE == stats->stageType); @@ -84,7 +84,7 @@ const DeleteStats* getDeleteStats(const PlanStageStats* stats) { // The stats may refer to a delete stage, or a projection stage wrapping a delete stage. if (StageType::STAGE_PROJECTION == stats->stageType) { invariant(stats->children.size() == 1); - stats = stats->children[0]; + stats = stats->children[0].get(); } invariant(StageType::STAGE_DELETE == stats->stageType); diff --git a/src/mongo/db/exec/and_hash.cpp b/src/mongo/db/exec/and_hash.cpp index 4518802afba..911ec6482cb 100644 --- a/src/mongo/db/exec/and_hash.cpp +++ b/src/mongo/db/exec/and_hash.cpp @@ -490,7 +490,7 @@ unique_ptr<PlanStageStats> AndHashStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_AND_HASH); ret->specific = make_unique<AndHashStats>(_specificStats); for (size_t i = 0; i < _children.size(); ++i) { - ret->children.push_back(_children[i]->getStats().release()); + ret->children.emplace_back(_children[i]->getStats()); } return ret; diff --git a/src/mongo/db/exec/and_sorted.cpp b/src/mongo/db/exec/and_sorted.cpp index 277d3424cf3..edc0450b214 100644 --- a/src/mongo/db/exec/and_sorted.cpp +++ b/src/mongo/db/exec/and_sorted.cpp @@ -290,7 +290,7 @@ unique_ptr<PlanStageStats> AndSortedStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_AND_SORTED); ret->specific = make_unique<AndSortedStats>(_specificStats); for (size_t i = 0; i < _children.size(); ++i) { - ret->children.push_back(_children[i]->getStats().release()); + ret->children.emplace_back(_children[i]->getStats()); } return ret; diff --git a/src/mongo/db/exec/cached_plan.cpp b/src/mongo/db/exec/cached_plan.cpp index a823a687792..dd2a89330fd 100644 --- a/src/mongo/db/exec/cached_plan.cpp +++ b/src/mongo/db/exec/cached_plan.cpp @@ -319,7 +319,7 @@ std::unique_ptr<PlanStageStats> CachedPlanStage::getStats() { std::unique_ptr<PlanStageStats> ret = stdx::make_unique<PlanStageStats>(_commonStats, STAGE_CACHED_PLAN); ret->specific = stdx::make_unique<CachedPlanStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/count.cpp b/src/mongo/db/exec/count.cpp index 83f7f952fae..269449d4a1b 100644 --- a/src/mongo/db/exec/count.cpp +++ b/src/mongo/db/exec/count.cpp @@ -167,7 +167,7 @@ unique_ptr<PlanStageStats> CountStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_COUNT); ret->specific = make_unique<CountStats>(_specificStats); if (!_children.empty()) { - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); } return ret; } diff --git a/src/mongo/db/exec/delete.cpp b/src/mongo/db/exec/delete.cpp index cd179d1a7d7..aacd2e65d62 100644 --- a/src/mongo/db/exec/delete.cpp +++ b/src/mongo/db/exec/delete.cpp @@ -266,7 +266,7 @@ unique_ptr<PlanStageStats> DeleteStage::getStats() { _commonStats.isEOF = isEOF(); unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_DELETE); ret->specific = make_unique<DeleteStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/fetch.cpp b/src/mongo/db/exec/fetch.cpp index 6e972a9346e..4088d1d10b3 100644 --- a/src/mongo/db/exec/fetch.cpp +++ b/src/mongo/db/exec/fetch.cpp @@ -237,7 +237,7 @@ unique_ptr<PlanStageStats> FetchStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_FETCH); ret->specific = make_unique<FetchStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/group.cpp b/src/mongo/db/exec/group.cpp index 43b8bf7ec6c..4ce1d4031db 100644 --- a/src/mongo/db/exec/group.cpp +++ b/src/mongo/db/exec/group.cpp @@ -290,7 +290,7 @@ unique_ptr<PlanStageStats> GroupStage::getStats() { _commonStats.isEOF = isEOF(); unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_GROUP); ret->specific = make_unique<GroupStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/keep_mutations.cpp b/src/mongo/db/exec/keep_mutations.cpp index d43cbb560f8..f1d38d033c6 100644 --- a/src/mongo/db/exec/keep_mutations.cpp +++ b/src/mongo/db/exec/keep_mutations.cpp @@ -125,7 +125,7 @@ unique_ptr<PlanStageStats> KeepMutationsStage::getStats() { _commonStats.isEOF = isEOF(); unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_KEEP_MUTATIONS); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/limit.cpp b/src/mongo/db/exec/limit.cpp index c4ea7485c3a..c595a455f25 100644 --- a/src/mongo/db/exec/limit.cpp +++ b/src/mongo/db/exec/limit.cpp @@ -99,7 +99,7 @@ unique_ptr<PlanStageStats> LimitStage::getStats() { _commonStats.isEOF = isEOF(); unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_LIMIT); ret->specific = make_unique<LimitStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/merge_sort.cpp b/src/mongo/db/exec/merge_sort.cpp index 98b6037d990..5c1bbb64e3a 100644 --- a/src/mongo/db/exec/merge_sort.cpp +++ b/src/mongo/db/exec/merge_sort.cpp @@ -254,7 +254,7 @@ unique_ptr<PlanStageStats> MergeSortStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SORT_MERGE); ret->specific = make_unique<MergeSortStats>(_specificStats); for (size_t i = 0; i < _children.size(); ++i) { - ret->children.push_back(_children[i]->getStats().release()); + ret->children.emplace_back(_children[i]->getStats()); } return ret; } diff --git a/src/mongo/db/exec/multi_plan.cpp b/src/mongo/db/exec/multi_plan.cpp index a00ea97769c..3075acd3aed 100644 --- a/src/mongo/db/exec/multi_plan.cpp +++ b/src/mongo/db/exec/multi_plan.cpp @@ -342,24 +342,6 @@ Status MultiPlanStage::pickBestPlan(PlanYieldPolicy* yieldPolicy) { return Status::OK(); } -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; - } - - unique_ptr<PlanStageStats> stats = _candidates[ix].root->getStats(); - candidateStats.push_back(stats.release()); - } - - return candidateStats.release(); -} - bool MultiPlanStage::workAllPlans(size_t numResults, PlanYieldPolicy* yieldPolicy) { bool doneWorking = false; @@ -498,15 +480,13 @@ QuerySolution* MultiPlanStage::bestSolution() { } unique_ptr<PlanStageStats> MultiPlanStage::getStats() { - if (bestPlanChosen()) { - return _candidates[_bestPlanIdx].root->getStats(); - } - if (hasBackupPlan()) { - return _candidates[_backupPlanIdx].root->getStats(); - } _commonStats.isEOF = isEOF(); - - return make_unique<PlanStageStats>(_commonStats, STAGE_MULTI_PLAN); + unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_MULTI_PLAN); + ret->specific = make_unique<MultiPlanStats>(_specificStats); + for (auto&& child : _children) { + ret->children.emplace_back(child->getStats()); + } + return ret; } const SpecificStats* MultiPlanStage::getSpecificStats() const { diff --git a/src/mongo/db/exec/multi_plan.h b/src/mongo/db/exec/multi_plan.h index 229aca6aa4c..0a9818b85cd 100644 --- a/src/mongo/db/exec/multi_plan.h +++ b/src/mongo/db/exec/multi_plan.h @@ -150,12 +150,6 @@ public: // Used by explain. // - /** - * Gathers execution stats for all losing plans. Caller takes ownership of - * all pointers in the returned vector. - */ - std::vector<PlanStageStats*> generateCandidateStats(); - static const char* kStageType; private: diff --git a/src/mongo/db/exec/near.cpp b/src/mongo/db/exec/near.cpp index 2095c22ae45..3a25432c2b9 100644 --- a/src/mongo/db/exec/near.cpp +++ b/src/mongo/db/exec/near.cpp @@ -313,7 +313,7 @@ unique_ptr<PlanStageStats> NearStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, _stageType); ret->specific.reset(_specificStats.clone()); for (size_t i = 0; i < _childrenIntervals.size(); ++i) { - ret->children.push_back(_childrenIntervals[i]->covering->getStats().release()); + ret->children.emplace_back(_childrenIntervals[i]->covering->getStats()); } return ret; } diff --git a/src/mongo/db/exec/or.cpp b/src/mongo/db/exec/or.cpp index 0bc195ca236..dae040ff9b5 100644 --- a/src/mongo/db/exec/or.cpp +++ b/src/mongo/db/exec/or.cpp @@ -162,7 +162,7 @@ unique_ptr<PlanStageStats> OrStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_OR); ret->specific = make_unique<OrStats>(_specificStats); for (size_t i = 0; i < _children.size(); ++i) { - ret->children.push_back(_children[i]->getStats().release()); + ret->children.emplace_back(_children[i]->getStats()); } return ret; diff --git a/src/mongo/db/exec/plan_stats.h b/src/mongo/db/exec/plan_stats.h index 8a7648bd4d7..14e07391920 100644 --- a/src/mongo/db/exec/plan_stats.h +++ b/src/mongo/db/exec/plan_stats.h @@ -106,12 +106,6 @@ private: struct PlanStageStats { PlanStageStats(const CommonStats& c, StageType t) : stageType(t), common(c) {} - ~PlanStageStats() { - for (size_t i = 0; i < children.size(); ++i) { - delete children[i]; - } - } - /** * Make a deep copy. */ @@ -122,7 +116,7 @@ struct PlanStageStats { } for (size_t i = 0; i < children.size(); ++i) { invariant(children[i]); - stats->children.push_back(children[i]->clone()); + stats->children.emplace_back(children[i]->clone()); } return stats; } @@ -137,7 +131,7 @@ struct PlanStageStats { std::unique_ptr<SpecificStats> specific; // The stats of the node's children. - std::vector<PlanStageStats*> children; + std::vector<std::unique_ptr<PlanStageStats>> children; private: MONGO_DISALLOW_COPYING(PlanStageStats); diff --git a/src/mongo/db/exec/projection.cpp b/src/mongo/db/exec/projection.cpp index 8f12efd53f6..a9e60bdace9 100644 --- a/src/mongo/db/exec/projection.cpp +++ b/src/mongo/db/exec/projection.cpp @@ -244,7 +244,7 @@ unique_ptr<PlanStageStats> ProjectionStage::getStats() { projStats->projObj = _projObj; ret->specific = std::move(projStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/shard_filter.cpp b/src/mongo/db/exec/shard_filter.cpp index e62b1bc8549..021e5c09c8b 100644 --- a/src/mongo/db/exec/shard_filter.cpp +++ b/src/mongo/db/exec/shard_filter.cpp @@ -136,7 +136,7 @@ unique_ptr<PlanStageStats> ShardFilterStage::getStats() { _commonStats.isEOF = isEOF(); unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SHARDING_FILTER); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); ret->specific = make_unique<ShardingFilterStats>(_specificStats); return ret; } diff --git a/src/mongo/db/exec/skip.cpp b/src/mongo/db/exec/skip.cpp index 15f829ed8bd..5c5a27a685f 100644 --- a/src/mongo/db/exec/skip.cpp +++ b/src/mongo/db/exec/skip.cpp @@ -102,7 +102,7 @@ unique_ptr<PlanStageStats> SkipStage::getStats() { _specificStats.skip = _toSkip; unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SKIP); ret->specific = make_unique<SkipStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/sort.cpp b/src/mongo/db/exec/sort.cpp index 6780e1bee82..b7b6fc0fc86 100644 --- a/src/mongo/db/exec/sort.cpp +++ b/src/mongo/db/exec/sort.cpp @@ -239,7 +239,7 @@ unique_ptr<PlanStageStats> SortStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SORT); ret->specific = make_unique<SortStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/sort_key_generator.cpp b/src/mongo/db/exec/sort_key_generator.cpp index 8c4d57482c0..b9a02b17ed2 100644 --- a/src/mongo/db/exec/sort_key_generator.cpp +++ b/src/mongo/db/exec/sort_key_generator.cpp @@ -315,7 +315,7 @@ PlanStage::StageState SortKeyGeneratorStage::work(WorkingSetID* out) { std::unique_ptr<PlanStageStats> SortKeyGeneratorStage::getStats() { auto ret = stdx::make_unique<PlanStageStats>(_commonStats, STAGE_SORT_KEY_GENERATOR); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp index 4e77c642ed4..ad84fb6869d 100644 --- a/src/mongo/db/exec/subplan.cpp +++ b/src/mongo/db/exec/subplan.cpp @@ -532,7 +532,7 @@ PlanStage::StageState SubplanStage::work(WorkingSetID* out) { unique_ptr<PlanStageStats> SubplanStage::getStats() { _commonStats.isEOF = isEOF(); unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_SUBPLAN); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/text.cpp b/src/mongo/db/exec/text.cpp index 2988fa1a305..5c2f1fa1e67 100644 --- a/src/mongo/db/exec/text.cpp +++ b/src/mongo/db/exec/text.cpp @@ -106,7 +106,7 @@ unique_ptr<PlanStageStats> TextStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_TEXT); ret->specific = make_unique<TextStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/text_match.cpp b/src/mongo/db/exec/text_match.cpp index 6e5ffe951e6..0dc2bf8b5a6 100644 --- a/src/mongo/db/exec/text_match.cpp +++ b/src/mongo/db/exec/text_match.cpp @@ -65,7 +65,7 @@ std::unique_ptr<PlanStageStats> TextMatchStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_TEXT_MATCH); ret->specific = make_unique<TextMatchStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/exec/text_or.cpp b/src/mongo/db/exec/text_or.cpp index dec75810a15..a022a7cce0d 100644 --- a/src/mongo/db/exec/text_or.cpp +++ b/src/mongo/db/exec/text_or.cpp @@ -122,8 +122,8 @@ std::unique_ptr<PlanStageStats> TextOrStage::getStats() { unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_TEXT_OR); ret->specific = make_unique<TextOrStats>(_specificStats); - for (auto& child : _children) { - ret->children.push_back(child->getStats().release()); + for (auto&& child : _children) { + ret->children.emplace_back(child->getStats()); } return ret; diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp index 59c25bdbc36..768aeebebfb 100644 --- a/src/mongo/db/exec/update.cpp +++ b/src/mongo/db/exec/update.cpp @@ -1000,7 +1000,7 @@ unique_ptr<PlanStageStats> UpdateStage::getStats() { _commonStats.isEOF = isEOF(); unique_ptr<PlanStageStats> ret = make_unique<PlanStageStats>(_commonStats, STAGE_UPDATE); ret->specific = make_unique<UpdateStats>(_specificStats); - ret->children.push_back(child()->getStats().release()); + ret->children.emplace_back(child()->getStats()); return ret; } diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp index 61cb9dfca0e..88984d6e2c1 100644 --- a/src/mongo/db/query/explain.cpp +++ b/src/mongo/db/query/explain.cpp @@ -60,16 +60,25 @@ using std::vector; * Traverse the tree rooted at 'root', and add all tree nodes into the list 'flattened'. */ void flattenStatsTree(const PlanStageStats* root, vector<const PlanStageStats*>* flattened) { + invariant(root->stageType != STAGE_MULTI_PLAN); flattened->push_back(root); - for (size_t i = 0; i < root->children.size(); ++i) { - flattenStatsTree(root->children[i], flattened); + for (auto&& child : root->children) { + flattenStatsTree(child.get(), flattened); } } /** - * Traverse the tree rooted at 'root', and add all nodes into the list 'flattened'. + * Traverse the tree rooted at 'root', and add all nodes into the list 'flattened'. If a + * MultiPlanStage is encountered, only add the best plan and its children to 'flattened'. */ void flattenExecTree(const PlanStage* root, vector<const PlanStage*>* flattened) { + if (root->stageType() == STAGE_MULTI_PLAN) { + // Only add the winning stage from a MultiPlanStage. + auto mps = static_cast<const MultiPlanStage*>(root); + const PlanStage* winningStage = mps->getChildren()[mps->bestPlanIdx()].get(); + return flattenExecTree(winningStage, flattened); + } + flattened->push_back(root); const auto& children = root->getChildren(); for (size_t i = 0; i < children.size(); ++i) { @@ -483,7 +492,7 @@ void Explain::statsToBSON(const PlanStageStats& stats, // static void Explain::generatePlannerInfo(PlanExecutor* exec, PlanStageStats* winnerStats, - const vector<PlanStageStats*>& rejectedStats, + const vector<unique_ptr<PlanStageStats>>& rejectedStats, BSONObjBuilder* out) { CanonicalQuery* query = exec->getCanonicalQuery(); @@ -596,8 +605,8 @@ void Explain::explainStages(PlanExecutor* exec, // 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. unique_ptr<PlanStageStats> winningStatsTrial; - if (verbosity >= ExplainCommon::EXEC_ALL_PLANS && NULL != mps) { - winningStatsTrial = exec->getStats(); + if (verbosity >= ExplainCommon::EXEC_ALL_PLANS && mps) { + winningStatsTrial = std::move(exec->getStats()->children[mps->bestPlanIdx()]); invariant(winningStatsTrial.get()); } @@ -611,13 +620,21 @@ void Explain::explainStages(PlanExecutor* exec, // Step 2: collect plan stats (which also give the structure of the plan tree). // - // Get stats for the winning plan. - unique_ptr<PlanStageStats> winningStats(exec->getStats()); + // Get stats for the winning plan. If there is only a single candidate plan, it is considered + // the winner. + unique_ptr<PlanStageStats> winningStats( + mps ? std::move(mps->getStats()->children[mps->bestPlanIdx()]) + : std::move(exec->getStats())); // Get stats for the rejected plans, if more than one plan was considered. - OwnedPointerVector<PlanStageStats> allPlansStats; - if (NULL != mps) { - allPlansStats = mps->generateCandidateStats(); + vector<unique_ptr<PlanStageStats>> allPlansStats; + if (mps && verbosity >= ExplainCommon::EXEC_ALL_PLANS) { + auto mpsStats = mps->getStats(); + for (size_t i = 0; i < mpsStats->children.size(); ++i) { + if (i != static_cast<size_t>(mps->bestPlanIdx())) { + allPlansStats.emplace_back(std::move(mpsStats->children[i])); + } + } } // @@ -625,7 +642,7 @@ void Explain::explainStages(PlanExecutor* exec, // if (verbosity >= ExplainCommon::QUERY_PLANNER) { - generatePlannerInfo(exec, winningStats.get(), allPlansStats.vector(), out); + generatePlannerInfo(exec, winningStats.get(), allPlansStats, out); } if (verbosity >= ExplainCommon::EXEC_STATS) { @@ -651,15 +668,15 @@ void Explain::explainStages(PlanExecutor* exec, // 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) { + if (mps) { invariant(winningStatsTrial.get()); - allPlansStats.push_back(winningStatsTrial.release()); + allPlansStats.emplace_back(std::move(winningStatsTrial)); } BSONArrayBuilder allPlansBob(execBob.subarrayStart("allPlansExecution")); for (size_t i = 0; i < allPlansStats.size(); ++i) { BSONObjBuilder planBob(allPlansBob.subobjStart()); - generateExecStats(allPlansStats[i], verbosity, &planBob, boost::none); + generateExecStats(allPlansStats[i].get(), verbosity, &planBob, boost::none); planBob.doneFast(); } allPlansBob.doneFast(); diff --git a/src/mongo/db/query/explain.h b/src/mongo/db/query/explain.h index 1177dc1601c..069d89d7cb4 100644 --- a/src/mongo/db/query/explain.h +++ b/src/mongo/db/query/explain.h @@ -164,10 +164,11 @@ private: * @param winnerStats -- the stats tree for the winning plan. * @param rejectedStats -- an array of stats trees, one per rejected plan */ - static void generatePlannerInfo(PlanExecutor* exec, - PlanStageStats* winnerStats, - const std::vector<PlanStageStats*>& rejectedStats, - BSONObjBuilder* out); + static void generatePlannerInfo( + PlanExecutor* exec, + PlanStageStats* winnerStats, + const std::vector<std::unique_ptr<PlanStageStats>>& rejectedStats, + BSONObjBuilder* out); /** * Generates the execution stats section for the stats tree 'stats', diff --git a/src/mongo/db/query/plan_ranker.cpp b/src/mongo/db/query/plan_ranker.cpp index b89b9fe3f70..ff8344da6a0 100644 --- a/src/mongo/db/query/plan_ranker.cpp +++ b/src/mongo/db/query/plan_ranker.cpp @@ -169,7 +169,7 @@ double computeSelectivity(const PlanStageStats* stats) { } else { double sum = 0; for (size_t i = 0; i < stats->children.size(); ++i) { - sum += computeSelectivity(stats->children[i]); + sum += computeSelectivity(stats->children[i].get()); } return sum; } @@ -180,7 +180,7 @@ bool hasStage(const StageType type, const PlanStageStats* stats) { return true; } for (size_t i = 0; i < stats->children.size(); ++i) { - if (hasStage(type, stats->children[i])) { + if (hasStage(type, stats->children[i].get())) { return true; } } diff --git a/src/mongo/dbtests/query_stage_multiplan.cpp b/src/mongo/dbtests/query_stage_multiplan.cpp index 70da6dbec58..d772c80ce5f 100644 --- a/src/mongo/dbtests/query_stage_multiplan.cpp +++ b/src/mongo/dbtests/query_stage_multiplan.cpp @@ -35,6 +35,7 @@ #include "mongo/db/exec/index_scan.h" #include "mongo/db/exec/multi_plan.h" #include "mongo/db/exec/plan_stage.h" +#include "mongo/db/exec/queued_data_stage.h" #include "mongo/db/json.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/operation_context_impl.h" @@ -287,6 +288,121 @@ public: } }; +// Test the structure and values of the explain output. +class MPSExplainAllPlans : public QueryStageMultiPlanBase { +public: + void run() { + // Insert a document to create the collection. + insert(BSON("x" << 1)); + + const int nDocs = 500; + + auto ws = stdx::make_unique<WorkingSet>(); + auto firstPlan = stdx::make_unique<QueuedDataStage>(&_txn, ws.get()); + auto secondPlan = stdx::make_unique<QueuedDataStage>(&_txn, ws.get()); + + for (int i = 0; i < nDocs; ++i) { + addMember(firstPlan.get(), ws.get(), BSON("x" << 1)); + + // Make the second plan slower by inserting a NEED_TIME between every result. + addMember(secondPlan.get(), ws.get(), BSON("x" << 1)); + secondPlan->pushBack(PlanStage::NEED_TIME); + } + + AutoGetCollectionForRead ctx(&_txn, nss.ns()); + + auto cq = uassertStatusOK(CanonicalQuery::canonicalize(nss, BSON("x" << 1))); + unique_ptr<MultiPlanStage> mps = + make_unique<MultiPlanStage>(&_txn, ctx.getCollection(), cq.get()); + + // Put each plan into the MultiPlanStage. Takes ownership of 'firstPlan' and 'secondPlan'. + auto firstSoln = stdx::make_unique<QuerySolution>(); + auto secondSoln = stdx::make_unique<QuerySolution>(); + mps->addPlan(firstSoln.release(), firstPlan.release(), ws.get()); + mps->addPlan(secondSoln.release(), secondPlan.release(), ws.get()); + + // Making a PlanExecutor chooses the best plan. + auto exec = uassertStatusOK(PlanExecutor::make( + &_txn, std::move(ws), std::move(mps), ctx.getCollection(), PlanExecutor::YIELD_MANUAL)); + + auto root = static_cast<MultiPlanStage*>(exec->getRootStage()); + ASSERT_TRUE(root->bestPlanChosen()); + // The first QueuedDataStage should have won. + ASSERT_EQ(root->bestPlanIdx(), 0); + + BSONObjBuilder bob; + Explain::explainStages(exec.get(), ExplainCommon::EXEC_ALL_PLANS, &bob); + BSONObj explained = bob.done(); + + ASSERT_EQ(explained["executionStats"]["nReturned"].Int(), nDocs); + ASSERT_EQ(explained["executionStats"]["executionStages"]["needTime"].Int(), 0); + auto allPlansStats = explained["executionStats"]["allPlansExecution"].Array(); + ASSERT_EQ(allPlansStats.size(), 2UL); + for (auto&& planStats : allPlansStats) { + int maxEvaluationResults = internalQueryPlanEvaluationMaxResults; + ASSERT_EQ(planStats["executionStages"]["stage"].String(), "QUEUED_DATA"); + if (planStats["executionStages"]["needTime"].Int() > 0) { + // This is the losing plan. Should only have advanced about half the time. + ASSERT_LT(planStats["nReturned"].Int(), maxEvaluationResults); + } else { + // This is the winning plan. Stats here should be from the trial period. + ASSERT_EQ(planStats["nReturned"].Int(), maxEvaluationResults); + } + } + } + +private: + /** + * Allocates a new WorkingSetMember with data 'dataObj' in 'ws', and adds the WorkingSetMember + * to 'qds'. + */ + void addMember(QueuedDataStage* qds, WorkingSet* ws, BSONObj dataObj) { + WorkingSetID id = ws->allocate(); + WorkingSetMember* wsm = ws->get(id); + wsm->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("x" << 1)); + wsm->transitionToOwnedObj(); + qds->pushBack(id); + } +}; + +// Test that the plan summary only includes stats from the winning plan. +// +// This is a regression test for SERVER-20111. +class MPSSummaryStats : public QueryStageMultiPlanBase { +public: + void run() { + const int N = 5000; + for (int i = 0; i < N; ++i) { + insert(BSON("foo" << (i % 10))); + } + + // Add two indices to give more plans. + addIndex(BSON("foo" << 1)); + addIndex(BSON("foo" << -1 << "bar" << 1)); + + AutoGetCollectionForRead ctx(&_txn, nss.ns()); + Collection* coll = ctx.getCollection(); + + // Create the executor (Matching all documents). + auto queryObj = BSON("foo" << BSON("$gte" << 0)); + auto cq = uassertStatusOK(CanonicalQuery::canonicalize(nss, queryObj)); + auto exec = + uassertStatusOK(getExecutor(&_txn, coll, std::move(cq), PlanExecutor::YIELD_MANUAL)); + + ASSERT_EQ(exec->getRootStage()->stageType(), STAGE_MULTI_PLAN); + + exec->executePlan(); + + PlanSummaryStats stats; + Explain::getSummaryStats(*exec, &stats); + + // If only the winning plan's stats are recorded, we should not have examined more than the + // total number of documents/index keys. + ASSERT_LTE(stats.totalDocsExamined, static_cast<size_t>(N)); + ASSERT_LTE(stats.totalKeysExamined, static_cast<size_t>(N)); + } +}; + class All : public Suite { public: All() : Suite("query_stage_multiplan") {} @@ -294,6 +410,8 @@ public: void setupTests() { add<MPSCollectionScanVsHighlySelectiveIXScan>(); add<MPSBackupPlan>(); + add<MPSExplainAllPlans>(); + add<MPSSummaryStats>(); } }; |