Skip to content

Commit 7ecd6bd

Browse files
henrinikkuEvergreen Agent
authored andcommitted
SERVER-83215 Add generic logic to support fast paths that bypass the optimizer
1 parent dd8ecb0 commit 7ecd6bd

13 files changed

+882
-9
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* This test verifies that the optimizer fast path is not used for queries that would otherwise be
3+
* eligible but contain operations the current fast path implementations don't support (e.g. a
4+
* projection).
5+
*/
6+
import {planHasStage} from "jstests/libs/analyze_plan.js";
7+
import {
8+
checkCascadesOptimizerEnabled,
9+
checkFastPathEnabled,
10+
} from "jstests/libs/optimizer_utils.js";
11+
12+
if (!checkCascadesOptimizerEnabled(db)) {
13+
jsTestLog("Skipping test because the optimizer is not enabled");
14+
quit();
15+
}
16+
17+
if (!checkFastPathEnabled(db)) {
18+
jsTestLog("Skipping test because fast paths are not enabled");
19+
quit();
20+
}
21+
22+
function assertNotUsingFastPath(explainCmd) {
23+
const explain = assert.commandWorked(explainCmd);
24+
assert(!planHasStage(db, explain, "FASTPATH"));
25+
}
26+
27+
const coll = db.non_eligible_queries;
28+
coll.drop();
29+
30+
{
31+
// Empty find with a projection should not use fast path.
32+
const explain = coll.explain().find({}, {b: 1}).finish();
33+
assertNotUsingFastPath(explain);
34+
}
35+
{
36+
// Empty find with a sort spec should not use fast path.
37+
const explain = coll.explain().find({}).sort({b: 1}).finish();
38+
assertNotUsingFastPath(explain);
39+
}
40+
{
41+
// Empty find with limit should not use fast path.
42+
const explain = coll.explain().find({}).limit(3).finish();
43+
assertNotUsingFastPath(explain);
44+
}
45+
{
46+
// Empty find with skip should not use fast path.
47+
const explain = coll.explain().find({}).skip(3).finish();
48+
assertNotUsingFastPath(explain);
49+
}
50+
{
51+
// Pipeline with $project should not use fast path.
52+
let explain = coll.explain().aggregate([{$match: {}}, {$project: {a: 1}}]);
53+
assertNotUsingFastPath(explain);
54+
55+
explain = coll.explain().aggregate([{$project: {a: 1}}]);
56+
assertNotUsingFastPath(explain);
57+
}
58+
{
59+
// Pipeline with $sort should not use fast path.
60+
let explain = coll.explain().aggregate([{$match: {}}, {$sort: {a: 1}}]);
61+
assertNotUsingFastPath(explain);
62+
63+
explain = coll.explain().aggregate([{$sort: {a: 1}}]);
64+
assertNotUsingFastPath(explain);
65+
}
66+
{
67+
// Pipeline with $limit should not use fast path.
68+
let explain = coll.explain().aggregate([{$match: {}}, {$limit: 3}]);
69+
assertNotUsingFastPath(explain);
70+
71+
explain = coll.explain().aggregate([{$limit: 3}]);
72+
assertNotUsingFastPath(explain);
73+
}
74+
{
75+
// Pipeline with $skip should not use fast path.
76+
let explain = coll.explain().aggregate([{$match: {}}, {$skip: 3}]);
77+
assertNotUsingFastPath(explain);
78+
79+
explain = coll.explain().aggregate([{$skip: 3}]);
80+
assertNotUsingFastPath(explain);
81+
}

jstests/libs/optimizer_utils.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ export function checkPlanCacheParameterization(theDB) {
1515
return false;
1616
}
1717

18+
export function checkFastPathEnabled(theDB) {
19+
const isDisabled =
20+
theDB.adminCommand({getParameter: 1, internalCascadesOptimizerDisableFastPath: 1})
21+
.internalCascadesOptimizerDisableFastPath;
22+
return !isDisabled;
23+
}
24+
1825
/**
1926
* Utility for checking if the experimental Cascades optimizer code path is enabled (checks
2027
* framework control for M4+).
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* This test verifies that the optimizer fast path is not used for queries against sharded
3+
* collections.
4+
*/
5+
import {planHasStage} from "jstests/libs/analyze_plan.js";
6+
7+
const bonsaiSettings = {
8+
internalQueryFrameworkControl: "tryBonsai",
9+
featureFlagCommonQueryFramework: true,
10+
// TODO SERVER-80582: Uncomment this setting. Some sharding code calls empty find on an
11+
// unsharded collection internally, which causes this test to fail as that fast path is not
12+
// implemented yet. internalCascadesOptimizerDisableFastPath: false,
13+
};
14+
15+
const st = new ShardingTest({
16+
shards: 2,
17+
mongos: 1,
18+
other: {
19+
shardOptions: {
20+
setParameter: {
21+
...bonsaiSettings,
22+
"failpoint.enableExplainInBonsai": tojson({mode: "alwaysOn"}),
23+
}
24+
},
25+
mongosOptions: {setParameter: {...bonsaiSettings}},
26+
}
27+
});
28+
29+
const db = st.getDB("test");
30+
31+
const coll = db[jsTestName()];
32+
coll.drop();
33+
34+
assert.commandWorked(coll.insertMany([...Array(100).keys()].map(i => {
35+
return {_id: i, a: 1};
36+
})));
37+
38+
st.shardColl(coll.getName(), {_id: 1}, {_id: 50}, {_id: 51});
39+
40+
function assertNotUsingFastPath(explainCmd) {
41+
const explain = assert.commandWorked(explainCmd);
42+
assert(!planHasStage(db, explain, "FASTPATH"));
43+
}
44+
45+
{
46+
// Empty find on a sharded collection should not use fast path.
47+
const explain = coll.explain().find().finish();
48+
assertNotUsingFastPath(explain);
49+
}
50+
{
51+
// Pipeline with empty match on a sharded collection should not use fast path.
52+
const explain = coll.explain().aggregate([{$match: {}}]);
53+
assertNotUsingFastPath(explain);
54+
}
55+
{
56+
// Empty aggregate on a sharded collection should not use fast path.
57+
const explain = coll.explain().aggregate([]);
58+
assertNotUsingFastPath(explain);
59+
}
60+
61+
st.stop();

src/mongo/db/SConscript

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,6 +1576,8 @@ execEnv.Library(
15761576
'query/classic_stage_builder.cpp',
15771577
'query/cost_model/on_coefficients_change_updater_impl.cpp',
15781578
'query/cqf_command_utils.cpp',
1579+
'query/cqf_fast_paths.cpp',
1580+
'query/cqf_fast_paths_utils.cpp',
15791581
'query/cqf_get_executor.cpp',
15801582
'query/explain.cpp',
15811583
'query/find.cpp',

src/mongo/db/commands/run_aggregate.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
#include "mongo/db/query/collation/collator_interface.h"
101101
#include "mongo/db/query/collection_query_info.h"
102102
#include "mongo/db/query/cqf_command_utils.h"
103+
#include "mongo/db/query/cqf_fast_paths.h"
103104
#include "mongo/db/query/cqf_get_executor.h"
104105
#include "mongo/db/query/cursor_response.h"
105106
#include "mongo/db/query/explain.h"
@@ -1436,13 +1437,28 @@ Status runAggregate(OperationContext* opCtx,
14361437
optimizer::QueryHints queryHints = getHintsFromQueryKnobs();
14371438
const bool fastIndexNullHandling = queryHints._fastIndexNullHandling;
14381439
auto timeBegin = Date_t::now();
1439-
auto maybeExec = getSBEExecutorViaCascadesOptimizer(opCtx,
1440-
expCtx,
1441-
nss,
1442-
collections,
1443-
std::move(queryHints),
1444-
request.getHint(),
1445-
pipeline.get());
1440+
auto maybeExec = [&] {
1441+
// If the query is eligible for a fast path, use the fast path plan instead of
1442+
// invoking the optimizer.
1443+
if (auto fastPathExec = optimizer::fast_path::tryGetSBEExecutorViaFastPath(
1444+
opCtx,
1445+
expCtx,
1446+
nss,
1447+
collections,
1448+
request.getExplain().has_value(),
1449+
request.getHint().has_value(),
1450+
pipeline.get())) {
1451+
return fastPathExec;
1452+
}
1453+
1454+
return getSBEExecutorViaCascadesOptimizer(opCtx,
1455+
expCtx,
1456+
nss,
1457+
collections,
1458+
std::move(queryHints),
1459+
request.getHint(),
1460+
pipeline.get());
1461+
}();
14461462
if (maybeExec) {
14471463
// Pass ownership of the pipeline to the executor. This is done to allow binding of
14481464
// parameters to use views onto the constants living in the MatchExpression (in the

src/mongo/db/query/SConscript

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ env.CppUnitTest(
449449
"ce_mode_parameter_test.cpp",
450450
"classic_stage_builder_test.cpp",
451451
"count_command_test.cpp",
452+
"cqf_fast_paths_test.cpp",
452453
"cursor_response_test.cpp",
453454
"query_shape/distinct_cmd_shape_test.cpp",
454455
"query_shape/find_cmd_shape_test.cpp",

0 commit comments

Comments
 (0)