summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2014-03-04 14:49:06 -0500
committerDavid Storch <david.storch@10gen.com>2014-03-05 10:12:57 -0500
commit37abe9e1814e5c6ab9c18970690b157dc13e9ede (patch)
tree2bba661750abc3e11c8d2473769d099987146449
parent91886492a3a107102234e183d6d937e19e54e6c1 (diff)
downloadmongo-37abe9e1814e5c6ab9c18970690b157dc13e9ede.tar.gz
SERVER-12939 prefer non-blocking sort plans when no plans produce or hit EOF
-rw-r--r--src/mongo/db/query/plan_ranker.cpp32
-rw-r--r--src/mongo/dbtests/plan_ranking.cpp40
2 files changed, 62 insertions, 10 deletions
diff --git a/src/mongo/db/query/plan_ranker.cpp b/src/mongo/db/query/plan_ranker.cpp
index 84884db9bdb..af110dcdefc 100644
--- a/src/mongo/db/query/plan_ranker.cpp
+++ b/src/mongo/db/query/plan_ranker.cpp
@@ -189,17 +189,23 @@ namespace mongo {
double productivity = static_cast<double>(stats->common.advanced)
/ static_cast<double>(workUnits);
- // If we have to perform a fetch, that's not great.
+ // Just enough to break a tie.
+ static const double epsilon = 0.0001;
+
+ // We prefer covered projections.
//
// We only do this when we have a projection stage because we have so many jstests that
// check bounds even when a collscan plan is just as good as the ixscan'd plan :(
- double noFetchBonus = 1;
- double epsilon = 0.001;
-
- // We prefer covered projections.
+ double noFetchBonus = epsilon;
if (hasStage(STAGE_PROJECTION, stats) && hasStage(STAGE_FETCH, stats)) {
- // Just enough to break a tie.
- noFetchBonus = 1 - epsilon;
+ noFetchBonus = 0;
+ }
+
+ // In the case of ties, prefer solutions without a blocking sort
+ // to solutions with a blocking sort.
+ double noSortBonus = epsilon;
+ if (hasStage(STAGE_SORT, stats)) {
+ noSortBonus = 0;
}
// In the case of ties, prefer single index solutions to ixisect. Index
@@ -216,7 +222,8 @@ namespace mongo {
noIxisectBonus = 0;
}
- double score = baseScore + productivity + noFetchBonus + noIxisectBonus;
+ double tieBreakers = noFetchBonus + noSortBonus + noIxisectBonus;
+ double score = baseScore + productivity + tieBreakers;
QLOG() << "score (" << score << ") = baseScore(" << baseScore << ")"
<< " + productivity((" << stats->common.advanced
@@ -226,8 +233,13 @@ namespace mongo {
<< stats->common.needFetch
<< " needFetch) = "
<< productivity << ")"
- << " + noFetchBonus(" << noFetchBonus << ")"
- << " + noIxisectBonus(" << noIxisectBonus << ")"
+ << " + tieBreakers(" << noFetchBonus
+ << " noFetchBonus + "
+ << noSortBonus
+ << " noSortBonus + "
+ << noIxisectBonus
+ << " noIxisectBonus = "
+ << tieBreakers << ")"
<< endl;
if (forceIntersectionPlans) {
diff --git a/src/mongo/dbtests/plan_ranking.cpp b/src/mongo/dbtests/plan_ranking.cpp
index 5d15a00fd45..1be5d0ad9d0 100644
--- a/src/mongo/dbtests/plan_ranking.cpp
+++ b/src/mongo/dbtests/plan_ranking.cpp
@@ -700,6 +700,45 @@ namespace PlanRankingTests {
}
};
+ /**
+ * When no other information is available, prefer solutions without
+ * a blocking sort stage.
+ */
+ class PlanRankingAvoidBlockingSort : public PlanRankingTestBase {
+ public:
+ void run() {
+ static const int N = 10000;
+
+ for (int i = 0; i < N; ++i) {
+ insert(BSON("a" << 1 << "d" << i));
+ }
+
+ // The index {d: 1, e: 1} provides the desired sort order,
+ // while index {a: 1, b: 1} can be used to answer the
+ // query predicate, but does not provide the sort.
+ addIndex(BSON("a" << 1 << "b" << 1));
+ addIndex(BSON("d" << 1 << "e" << 1));
+
+ // Query: find({a: 1}).sort({d: 1})
+ CanonicalQuery* cq;
+ ASSERT(CanonicalQuery::canonicalize(ns,
+ BSON("a" << 1),
+ BSON("d" << 1), // sort
+ BSONObj(), // projection
+ &cq).isOK());
+ ASSERT(NULL != cq);
+
+ // No results will be returned during the trial period,
+ // so we expect to choose {d: 1, e: 1}, as it allows us
+ // to avoid the sort stage.
+ QuerySolution* soln = pickBestPlan(cq);
+ ASSERT(QueryPlannerTestLib::solutionMatches(
+ "{fetch: {filter: {a:1}, node: "
+ "{ixscan: {filter: null, pattern: {d:1,e:1}}}}}",
+ soln->root.get()));
+ }
+ };
+
class All : public Suite {
public:
All() : Suite( "query_plan_ranking" ) {}
@@ -718,6 +757,7 @@ namespace PlanRankingTests {
add<PlanRankingNonCoveredIxisectFetchesLess>();
add<PlanRankingIxisectHitsEOFFirst>();
add<PlanRankingChooseBetweenIxisectPlans>();
+ add<PlanRankingAvoidBlockingSort>();
}
} planRankingAll;