summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/multi_plan_runner.cpp
diff options
context:
space:
mode:
authorHari Khalsa <hkhalsa@10gen.com>2013-10-29 21:41:09 -0400
committerHari Khalsa <hkhalsa@10gen.com>2013-10-30 16:07:48 -0400
commit67defc7ee10099e727a6091bbd0ff154e2908b00 (patch)
tree43abaedf20ddf0c58e0bf02eebdb92e3622477e8 /src/mongo/db/query/multi_plan_runner.cpp
parenta154946a48b3fea77747f810ff60dce734e9b0dd (diff)
downloadmongo-67defc7ee10099e727a6091bbd0ff154e2908b00.tar.gz
SERVER-10026 sort with improved sort analysis
Diffstat (limited to 'src/mongo/db/query/multi_plan_runner.cpp')
-rw-r--r--src/mongo/db/query/multi_plan_runner.cpp93
1 files changed, 86 insertions, 7 deletions
diff --git a/src/mongo/db/query/multi_plan_runner.cpp b/src/mongo/db/query/multi_plan_runner.cpp
index cee86fe8899..e60f83290bc 100644
--- a/src/mongo/db/query/multi_plan_runner.cpp
+++ b/src/mongo/db/query/multi_plan_runner.cpp
@@ -42,8 +42,13 @@
namespace mongo {
MultiPlanRunner::MultiPlanRunner(CanonicalQuery* query)
- : _killed(false), _failure(false), _failureCount(0), _policy(Runner::YIELD_MANUAL),
- _query(query) { }
+ : _killed(false),
+ _failure(false),
+ _failureCount(0),
+ _policy(Runner::YIELD_MANUAL),
+ _query(query),
+ _backupSolution(NULL),
+ _backupPlan(NULL) { }
MultiPlanRunner::~MultiPlanRunner() {
for (size_t i = 0; i < _candidates.size(); ++i) {
@@ -53,6 +58,14 @@ namespace mongo {
delete _candidates[i].ws;
}
+ if (NULL != _backupSolution) {
+ delete _backupSolution;
+ }
+
+ if (NULL != _backupPlan) {
+ delete _backupPlan;
+ }
+
for (vector<PlanStageStats*>::iterator it = _candidateStats.begin();
it != _candidateStats.end();
++it) {
@@ -71,6 +84,9 @@ namespace mongo {
if (NULL != _bestPlan) {
_bestPlan->setYieldPolicy(policy);
+ if (NULL != _backupPlan) {
+ _backupPlan->setYieldPolicy(policy);
+ }
} else {
// Still running our candidates and doing our own yielding.
if (Runner::YIELD_MANUAL == policy) {
@@ -87,6 +103,9 @@ namespace mongo {
if (NULL != _bestPlan) {
_bestPlan->saveState();
+ if (NULL != _backupPlan) {
+ _backupPlan->saveState();
+ }
}
else {
allPlansSaveState();
@@ -98,6 +117,9 @@ namespace mongo {
if (NULL != _bestPlan) {
return _bestPlan->restoreState();
+ if (NULL != _backupPlan) {
+ _backupPlan->restoreState();
+ }
}
else {
allPlansRestoreState();
@@ -110,22 +132,41 @@ namespace mongo {
if (NULL != _bestPlan) {
_bestPlan->invalidate(dl);
- for (deque<WorkingSetID>::iterator it = _alreadyProduced.begin();
- it != _alreadyProduced.end(); ++it) {
+ for (list<WorkingSetID>::iterator it = _alreadyProduced.begin();
+ it != _alreadyProduced.end();) {
WorkingSetMember* member = _bestPlan->getWorkingSet()->get(*it);
if (member->hasLoc() && member->loc == dl) {
+ list<WorkingSetID>::iterator next = it;
+ next++;
WorkingSetCommon::fetchAndInvalidateLoc(member);
+ _bestPlan->getWorkingSet()->flagForReview(*it);
+ _alreadyProduced.erase(it);
+ it = next;
+ }
+ else {
+ it++;
}
}
+ if (NULL != _backupPlan) {
+ _backupPlan->invalidate(dl);
+ }
}
else {
for (size_t i = 0; i < _candidates.size(); ++i) {
_candidates[i].root->invalidate(dl);
- for (deque<WorkingSetID>::iterator it = _candidates[i].results.begin();
- it != _candidates[i].results.end(); ++it) {
+ for (list<WorkingSetID>::iterator it = _candidates[i].results.begin();
+ it != _candidates[i].results.end();) {
WorkingSetMember* member = _candidates[i].ws->get(*it);
if (member->hasLoc() && member->loc == dl) {
+ list<WorkingSetID>::iterator next = it;
+ next++;
WorkingSetCommon::fetchAndInvalidateLoc(member);
+ _candidates[i].ws->flagForReview(*it);
+ _candidates[i].results.erase(it);
+ it = next;
+ }
+ else {
+ it++;
}
}
}
@@ -199,7 +240,26 @@ namespace mongo {
return Runner::RUNNER_ADVANCED;
}
- return _bestPlan->getNext(objOut, dlOut);
+ RunnerState state = _bestPlan->getNext(objOut, dlOut);
+
+ if (Runner::RUNNER_ERROR == state && (NULL != _backupSolution)) {
+ QLOG() << "Best plan errored out switching to backup\n";
+ _bestPlan.reset(_backupPlan);
+ _backupPlan = NULL;
+ _bestSolution.reset(_backupSolution);
+ _backupSolution = NULL;
+ return _bestPlan->getNext(objOut, dlOut);
+ }
+
+ if (NULL != _backupSolution && Runner::RUNNER_ADVANCED == state) {
+ QLOG() << "Best plan had a blocking sort, became unblocked, deleting backup plan\n";
+ delete _backupSolution;
+ delete _backupPlan;
+ _backupSolution = NULL;
+ _backupPlan = NULL;
+ }
+
+ return state;
}
bool MultiPlanRunner::pickBestPlan(size_t* out) {
@@ -224,6 +284,23 @@ namespace mongo {
QLOG() << "Winning solution:\n" << _bestSolution->toString() << endl;
+ size_t backupChild = bestChild;
+ if (_bestSolution->hasSortStage && (0 == _alreadyProduced.size())) {
+ QLOG() << "Winner has blocked sort, looking for backup plan...\n";
+ for (size_t i = 0; i < _candidates.size(); ++i) {
+ // TODO: if we drastically change plan ranking, this will die.
+ verify(0 == _candidates[i].results.size());
+ if (!_candidates[i].solution->hasSortStage) {
+ QLOG() << "Candidate " << i << " is backup child\n";
+ backupChild = i;
+ _backupSolution = _candidates[i].solution;
+ _backupPlan = new PlanExecutor(_candidates[i].ws, _candidates[i].root);
+ _backupPlan->setYieldPolicy(_policy);
+ break;
+ }
+ }
+ }
+
// TODO:
// Store the choice we just made in the cache.
// QueryPlanCache* cache = PlanCache::get(somenamespace);
@@ -233,6 +310,8 @@ namespace mongo {
// Clear out the candidate plans, leaving only stats as we're all done w/them.
for (size_t i = 0; i < _candidates.size(); ++i) {
if (i == bestChild) { continue; }
+ if (i == backupChild) { continue; }
+
delete _candidates[i].solution;
// Remember the stats for the candidate plan because we always show it on an