Permalink
Browse files

SERVER-12868 Pick backup plan when winning plan contains hashed AND s…

…tage
  • Loading branch information...
1 parent 80fcae0 commit ba9db2cc41b42e8867d84d14761659fe292d5a95 @benety benety committed Feb 26, 2014
@@ -430,6 +430,10 @@ namespace mongo {
return true;
}
+ bool MultiPlanRunner::hasBackupPlan() const {
+ return NULL != _backupPlan;
+ }
+
bool MultiPlanRunner::workAllPlans(BSONObj* objOut) {
bool planHitEOF = false;
@@ -85,6 +85,13 @@ namespace mongo {
*/
bool pickBestPlan(size_t* out, BSONObj* objOut);
+ /**
+ * Returns true if a backup plan was picked.
+ * This is the case when the best plan has a blocking stage.
+ * Exposed for testing.
+ */
+ bool hasBackupPlan() const;
+
virtual void saveState();
virtual bool restoreState();
virtual void invalidate(const DiskLoc& dl, InvalidationType type);
@@ -449,12 +449,14 @@ namespace mongo {
bool hasSortStage = false;
solnRoot = analyzeSort(query, params, solnRoot, &hasSortStage);
- // A solution can be blocking if it has a blocking sort stage.
- soln->hasBlockingStage = hasSortStage;
-
// This can happen if we need to create a blocking sort stage and we're not allowed to.
if (NULL == solnRoot) { return NULL; }
+ // A solution can be blocking if it has a blocking sort stage or
+ // a hashed AND stage.
+ bool hasAndHashStage = hasNode(solnRoot, STAGE_AND_HASH);
+ soln->hasBlockingStage = hasSortStage || hasAndHashStage;
+
// If we can (and should), add the keep mutations stage.
// We cannot keep mutated documents if:
@@ -479,7 +481,7 @@ namespace mongo {
// Only these stages can produce flagged results. A stage has to hold state past one call
// to work(...) in order to possibly flag a result.
bool couldProduceFlagged = hasNode(solnRoot, STAGE_GEO_2D)
- || hasNode(solnRoot, STAGE_AND_HASH)
+ || hasAndHashStage
|| hasNode(solnRoot, STAGE_AND_SORTED)
|| hasNode(solnRoot, STAGE_FETCH);
@@ -57,11 +57,16 @@ namespace PlanRankingTests {
class PlanRankingTestBase {
public:
- PlanRankingTestBase() {
+ PlanRankingTestBase() : _forceIntersectionPlans(forceIntersectionPlans) {
Client::WriteContext ctx(ns);
_client.dropCollection(ns);
}
+ virtual ~PlanRankingTestBase() {
+ // Restore external setParameter testing bool.
+ forceIntersectionPlans = _forceIntersectionPlans;
+ }
+
void insert(const BSONObj& obj) {
Client::WriteContext ctx(ns);
_client.insert(ns, obj);
@@ -126,9 +131,20 @@ namespace PlanRankingTests {
return solutions[bestPlan];
}
+ /**
+ * Was a backup plan picked during the ranking process?
+ */
+ bool hasBackupPlan() const {
+ ASSERT(NULL != _mpr.get());
+ return _mpr->hasBackupPlan();
+ }
+
private:
static DBDirectClient _client;
scoped_ptr<MultiPlanRunner> _mpr;
+ // Holds the value of global "forceIntersectionPlans" setParameter flag.
+ // Restored at end of test invocation regardless of test result.
+ bool _forceIntersectionPlans;
};
DBDirectClient PlanRankingTestBase::_client;
@@ -163,6 +179,7 @@ namespace PlanRankingTests {
soln->root.get()));
// Turn on the "force intersect" option.
+ // This will be reverted by PlanRankingTestBase's destructor when the test completes.
forceIntersectionPlans = true;
// And run the same query again.
@@ -178,9 +195,46 @@ namespace PlanRankingTests {
"{ixscan: {filter: null, pattern: {a:1}}},"
"{ixscan: {filter: null, pattern: {b:1}}}]}}}}",
soln->root.get()));
+ }
+ };
+
+ /**
+ * Test that a hashed AND solution plan is picked along with a non-blocking backup solution.
+ */
+ class PlanRankingIntersectWithBackup : public PlanRankingTestBase {
+ public:
+ void run() {
+ static const int N = 10000;
+
+ // 'a' is very selective, 'b' is not.
+ for (int i = 0; i < N; ++i) {
+ insert(BSON("a" << i << "b" << 1));
+ }
+
+ // Add indices on 'a' and 'b'.
+ addIndex(BSON("a" << 1));
+ addIndex(BSON("b" << 1));
+
+ // Run the query {a:1, b:{$gt:1}.
+ CanonicalQuery* cq;
+ verify(CanonicalQuery::canonicalize(ns, BSON("a" << 1 << "b" << BSON("$gt" << 1)),
+ &cq).isOK());
+ ASSERT(NULL != cq);
+
+ // Turn on the "force intersect" option.
+ // This will be reverted by PlanRankingTestBase's destructor when the test completes.
+ forceIntersectionPlans = true;
+
+ // Takes ownership of cq.
+ QuerySolution* soln = pickBestPlan(cq);
+ ASSERT(QueryPlannerTestLib::solutionMatches(
+ "{fetch: {filter: null, node: {andHash: {nodes: ["
+ "{ixscan: {filter: null, pattern: {a:1}}},"
+ "{ixscan: {filter: null, pattern: {b:1}}}]}}}}",
+ soln->root.get()));
- // Turn off the intersection forcing flag for future tests.
- forceIntersectionPlans = false;
+ // Confirm that a backup plan is available.
+ ASSERT(hasBackupPlan());
}
};
@@ -405,6 +459,7 @@ namespace PlanRankingTests {
void setupTests() {
add<PlanRankingIntersectOverride>();
+ add<PlanRankingIntersectWithBackup>();
add<PlanRankingPreferCovered>();
add<PlanRankingAvoidIntersectIfNoResults>();
add<PlanRankingPreferCoveredEvenIfNoResults>();

0 comments on commit ba9db2c

Please sign in to comment.