SERVER-117390: Add costs to join explain output (#46771)
GitOrigin-RevId: 28b807193fbb173023fbc427b8b0442f25151d99
This commit is contained in:
parent
7f877a2cfa
commit
fcb2dea011
@ -195,7 +195,14 @@ export function normalizePlan(plan, flatten = true) {
|
||||
}
|
||||
|
||||
// Expand this array if you find new fields which are inconsistent across different test runs.
|
||||
const ignoreFields = ["isCached", "indexVersion", "planNodeId", "cardinalityEstimate", "estimatesMetadata"];
|
||||
const ignoreFields = [
|
||||
"isCached",
|
||||
"indexVersion",
|
||||
"planNodeId",
|
||||
"cardinalityEstimate",
|
||||
"costEstimate",
|
||||
"estimatesMetadata",
|
||||
];
|
||||
|
||||
// Iterates over the plan while ignoring the `ignoreFields`, to create flattened stages whenever
|
||||
// `kExplainChildFieldNames` are encountered.
|
||||
|
||||
@ -54,9 +54,15 @@ function runTest(pipeline) {
|
||||
if (stage.stage.includes("JOIN_EMBEDDING") || stage.stage.includes("COLLSCAN")) {
|
||||
assert(
|
||||
stage.hasOwnProperty("cardinalityEstimate"),
|
||||
"Estimates not found in stage: " + tojson(stage) + ", " + tojson(explain),
|
||||
"Cardinality estimate not found in stage: " + tojson(stage) + ", " + tojson(explain),
|
||||
);
|
||||
assert.gt(stage.cardinalityEstimate, 0, "Cardinality estimate is not greater than 0");
|
||||
assert(
|
||||
stage.hasOwnProperty("costEstimate"),
|
||||
"Cost estimate not found in stage: " + tojson(stage) + ", " + tojson(explain),
|
||||
);
|
||||
// TODO SERVER-117480: Change this assert to be strictly greater than zero.
|
||||
assert.gte(stage.costEstimate, 0, "Cost estimate is not greater than 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,18 +155,19 @@ std::vector<QSNJoinPredicate> makeJoinPreds(const JoinReorderingContext& ctx,
|
||||
return preds;
|
||||
}
|
||||
|
||||
void addEstimateIfExplain(const JoinReorderingContext& ctx,
|
||||
const PlanEnumeratorContext& peCtx,
|
||||
QuerySolutionNode* node,
|
||||
NodeSet set,
|
||||
cost_based_ranker::EstimateMap& estimates) {
|
||||
void addEstimatesIfExplain(const JoinReorderingContext& ctx,
|
||||
const PlanEnumeratorContext& peCtx,
|
||||
QuerySolutionNode* node,
|
||||
NodeSet set,
|
||||
const JoinCostEstimate& cost,
|
||||
cost_based_ranker::EstimateMap& estimates) {
|
||||
if (!ctx.explain) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO SERVER-116505: Populate estimates map with cost information when available.
|
||||
auto ce = peCtx.getJoinCardinalityEstimator()->getOrEstimateSubsetCardinality(set);
|
||||
estimates.emplace(node, cost_based_ranker::QSNEstimate{.outCE = ce});
|
||||
estimates.emplace(node,
|
||||
cost_based_ranker::QSNEstimate{.outCE = ce, .cost = cost.getTotalCost()});
|
||||
}
|
||||
|
||||
// Forward-declare because of mutual recursion.
|
||||
@ -181,21 +182,22 @@ std::unique_ptr<QuerySolutionNode> buildQSNFromJoinPlan(const JoinReorderingCont
|
||||
JoinPlanNodeId nodeId,
|
||||
cost_based_ranker::EstimateMap& estimates) {
|
||||
std::unique_ptr<QuerySolutionNode> qsn;
|
||||
std::visit(
|
||||
OverloadedVisitor{[&](const JoiningNode& join) {
|
||||
qsn = buildQSNFromJoiningNode(ctx, peCtx, join, estimates);
|
||||
addEstimateIfExplain(ctx, peCtx, qsn.get(), join.bitset, estimates);
|
||||
},
|
||||
[&](const BaseNode& base) {
|
||||
// TODO SERVER-111913: Avoid this clone
|
||||
qsn = base.soln->root()->clone();
|
||||
addEstimateIfExplain(
|
||||
ctx, peCtx, qsn.get(), NodeSet().set(base.node), estimates);
|
||||
},
|
||||
[&](const INLJRHSNode& ip) {
|
||||
qsn = createIndexProbeQSN(ctx.joinGraph.getNode(ip.node), ip.entry);
|
||||
}},
|
||||
peCtx.registry().get(nodeId));
|
||||
std::visit(OverloadedVisitor{
|
||||
[&](const JoiningNode& join) {
|
||||
qsn = buildQSNFromJoiningNode(ctx, peCtx, join, estimates);
|
||||
addEstimatesIfExplain(
|
||||
ctx, peCtx, qsn.get(), join.bitset, join.cost, estimates);
|
||||
},
|
||||
[&](const BaseNode& base) {
|
||||
// TODO SERVER-111913: Avoid this clone
|
||||
qsn = base.soln->root()->clone();
|
||||
addEstimatesIfExplain(
|
||||
ctx, peCtx, qsn.get(), NodeSet().set(base.node), base.cost, estimates);
|
||||
},
|
||||
[&](const INLJRHSNode& ip) {
|
||||
qsn = createIndexProbeQSN(ctx.joinGraph.getNode(ip.node), ip.entry);
|
||||
}},
|
||||
peCtx.registry().get(nodeId));
|
||||
return qsn;
|
||||
}
|
||||
|
||||
|
||||
@ -295,8 +295,7 @@ void statsToBSON(const QuerySolutionNode* node,
|
||||
// Cost and cardinality of the stage.
|
||||
if (estimates.contains(node)) {
|
||||
const auto& est = estimates.at(node);
|
||||
// TODO SERVER-116505: Add cost here when available, possibly differentiating costs from the
|
||||
// join module vs CBR.
|
||||
bob->append("costEstimate", est.cost.toDouble());
|
||||
bob->append("cardinalityEstimate", est.outCE.toDouble());
|
||||
BSONObjBuilder metadataBob(bob->subobjStart("estimatesMetadata"));
|
||||
metadataBob.append("ceSource", toStringData(est.outCE.source()));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user