summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough
diff options
context:
space:
mode:
authorDavid Storch <david.storch@mongodb.com>2022-01-27 14:53:05 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-01-27 15:53:17 +0000
commitf27f088ecf14825a2ae9cedb2c13093287ded84a (patch)
treeb38f12dcf2e9ad7a5c9e2a0b8546ff0cd69e6f2d /jstests/noPassthrough
parent2891359b151883d41ceb3b79d769867462b79e89 (diff)
downloadmongo-f27f088ecf14825a2ae9cedb2c13093287ded84a.tar.gz
SERVER-62981 Make SBE multi-planner trial period length independent of collection size
This patch changes the 'internalQueryPlanEvaluationCollFraction' knob to apply only to the classic engine. It introduces a separate knob, 'internalQueryPlanEvaluationCollFractionSbe', which applies only to the SBE engine. The SBE knob has a default of 0, while the classic engine retains its default of 0.3. This ensures that by default, no candidate plan will ever do more than 10,000 storage reads during SBE multi-planning.
Diffstat (limited to 'jstests/noPassthrough')
-rw-r--r--jstests/noPassthrough/query_knobs_validation.js14
-rw-r--r--jstests/noPassthrough/sbe_multiplanner_trial_termination.js91
2 files changed, 100 insertions, 5 deletions
diff --git a/jstests/noPassthrough/query_knobs_validation.js b/jstests/noPassthrough/query_knobs_validation.js
index 119d2029c23..219e9ce2e58 100644
--- a/jstests/noPassthrough/query_knobs_validation.js
+++ b/jstests/noPassthrough/query_knobs_validation.js
@@ -13,6 +13,7 @@ const testDB = conn.getDB("admin");
const expectedParamDefaults = {
internalQueryPlanEvaluationWorks: 10000,
internalQueryPlanEvaluationCollFraction: 0.3,
+ internalQueryPlanEvaluationCollFractionSbe: 0.0,
internalQueryPlanEvaluationMaxResults: 101,
internalQueryCacheMaxEntriesPerCollection: 5000,
// This is a deprecated alias for "internalQueryCacheMaxEntriesPerCollection".
@@ -89,11 +90,14 @@ assertSetParameterSucceeds("internalQueryPlanEvaluationWorks", 11);
assertSetParameterFails("internalQueryPlanEvaluationWorks", 0);
assertSetParameterFails("internalQueryPlanEvaluationWorks", -1);
-assertSetParameterSucceeds("internalQueryPlanEvaluationCollFraction", 0.0);
-assertSetParameterSucceeds("internalQueryPlanEvaluationCollFraction", 0.444);
-assertSetParameterSucceeds("internalQueryPlanEvaluationCollFraction", 1.0);
-assertSetParameterFails("internalQueryPlanEvaluationCollFraction", -0.1);
-assertSetParameterFails("internalQueryPlanEvaluationCollFraction", 1.0001);
+for (let paramName of ["internalQueryPlanEvaluationCollFraction",
+ "internalQueryPlanEvaluationCollFractionSbe"]) {
+ assertSetParameterSucceeds(paramName, 0.0);
+ assertSetParameterSucceeds(paramName, 0.444);
+ assertSetParameterSucceeds(paramName, 1.0);
+ assertSetParameterFails(paramName, -0.1);
+ assertSetParameterFails(paramName, 1.0001);
+}
assertSetParameterSucceeds("internalQueryPlanEvaluationMaxResults", 11);
assertSetParameterSucceeds("internalQueryPlanEvaluationMaxResults", 0);
diff --git a/jstests/noPassthrough/sbe_multiplanner_trial_termination.js b/jstests/noPassthrough/sbe_multiplanner_trial_termination.js
new file mode 100644
index 00000000000..8918df3bdb1
--- /dev/null
+++ b/jstests/noPassthrough/sbe_multiplanner_trial_termination.js
@@ -0,0 +1,91 @@
+/**
+ * Tests the logic around the termination condition for the SBE multiplanner. In particular,
+ * demonstrates that unlike the classic multiplanner, the SBE multiplanner's end condition is by
+ * default not proportional to the size of the collection.
+ */
+(function() {
+"use strict";
+
+const numDocs = 1000;
+const dbName = "sbe_multiplanner_db";
+const collName = "sbe_multiplanner_coll";
+const collFracKnob = "internalQueryPlanEvaluationCollFraction";
+const collFracKnobSbe = "internalQueryPlanEvaluationCollFractionSbe";
+const worksKnob = "internalQueryPlanEvaluationWorks";
+
+const defaultCollFrac = 0.3;
+const trialLengthFromCollFrac = defaultCollFrac * numDocs;
+const trialLengthFromWorksKnob = 0.1 * numDocs;
+
+const conn = MongoRunner.runMongod({});
+assert.neq(conn, null, "mongod failed to start");
+const db = conn.getDB(dbName);
+const coll = db[collName];
+
+// Gets the "allPlansExecution" section from the explain of a query that has zero results, but for
+// which the only two available indexed plans are highly unselective.
+//
+// Also asserts that the explain has the given version number.
+function getAllPlansExecution(explainVersion) {
+ const explain = coll.find({a: 1, b: 1, c: 1}).explain("allPlansExecution");
+ assert.eq(explain.explainVersion, explainVersion, explain);
+ assert(explain.hasOwnProperty("executionStats"), explain);
+ const execStats = explain.executionStats;
+ assert(execStats.hasOwnProperty("allPlansExecution"), explain);
+ return execStats.allPlansExecution;
+}
+
+// Create a collection with two indices, where neither index is helpful in answering the query.
+assert.commandWorked(coll.createIndex({a: 1}));
+assert.commandWorked(coll.createIndex({b: 1}));
+for (let i = 0; i < numDocs; ++i) {
+ assert.commandWorked(coll.insert({a: 1, b: 1}));
+}
+
+// Lower the value of the 'internalQueryPlanEvaluationWorks' so that it is smaller than 30% of the
+// collection. Since the classic multiplanner takes either the works limit or 30% of the collection
+// size -- whichever is larger -- this should cause the trial period to run for about 0.3 * numDocs
+// work cycles.
+const getParamRes = assert.commandWorked(db.adminCommand({getParameter: 1, [collFracKnob]: 1}));
+assert.eq(getParamRes[collFracKnob], defaultCollFrac);
+assert.commandWorked(db.adminCommand({setParameter: 1, [worksKnob]: trialLengthFromWorksKnob}));
+
+// Force the classic engine and run an "allPlansExecution" verbosity explain. Confirm that the trial
+// period terminates based on the the "collection fraction" as opposed to
+// 'internalQueryPlanEvaluationWorks'.
+assert.commandWorked(db.adminCommand({setParameter: 1, internalQueryForceClassicEngine: true}));
+let allPlans = getAllPlansExecution("1");
+for (let plan of allPlans) {
+ assert(plan.hasOwnProperty("executionStages"), plan);
+ const executionStages = plan.executionStages;
+ assert(executionStages.hasOwnProperty("works"), plan);
+ assert.eq(executionStages.works, trialLengthFromCollFrac, plan);
+}
+
+// Verifies that for each SBE plan in the 'allPlans' array, the number of storage reads done by the
+// plan is equal to 'expectedNumReads'.
+function verifySbeNumReads(allPlans, expectedNumReads) {
+ for (let plan of allPlans) {
+ // Infer the number of reads (SBE's equivalent of work units) as the sum of keys and
+ // documents examined.
+ assert(plan.hasOwnProperty("totalKeysExamined"), plan);
+ assert(plan.hasOwnProperty("totalDocsExamined"), plan);
+ const numReads = plan.totalKeysExamined + plan.totalDocsExamined;
+ assert.eq(numReads, expectedNumReads, plan);
+ }
+}
+
+// Allow the query to use SBE. This time, the trial period should terminate based on the works knob.
+assert.commandWorked(db.adminCommand({setParameter: 1, internalQueryForceClassicEngine: false}));
+allPlans = getAllPlansExecution("2");
+verifySbeNumReads(allPlans, trialLengthFromWorksKnob);
+
+// If the SBE "collection fraction" knob is set to the same value as the equivalent knob for the
+// classic engine, then the SBE trial period should now terminate based on this new value of the
+// collection fraction knob.
+assert.commandWorked(db.adminCommand({setParameter: 1, [collFracKnobSbe]: defaultCollFrac}));
+allPlans = getAllPlansExecution("2");
+verifySbeNumReads(allPlans, trialLengthFromCollFrac);
+
+MongoRunner.stopMongod(conn);
+}());