summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Shteinfeld <ben.shteinfeld@mongodb.com>2022-11-08 21:40:31 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-11-08 22:33:43 +0000
commitbae712c9891ad30c4fa9e1413efa4b3f3aa5c828 (patch)
treeb73222332a3b0de6067b94a3a710a03d1fa827d6
parent7a45a7307f2aac8389a0cb0a9b805e28686b4874 (diff)
downloadmongo-bae712c9891ad30c4fa9e1413efa4b3f3aa5c828.tar.gz
SERVER-68847 Include query framework information in GetMore profiler entries
-rw-r--r--jstests/noPassthrough/query_engine_stats.js72
-rw-r--r--src/mongo/db/commands/getmore_cmd.cpp2
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp7
-rw-r--r--src/mongo/db/curop.cpp54
-rw-r--r--src/mongo/db/curop.h13
-rw-r--r--src/mongo/db/pipeline/document_source_cursor.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_cursor.h6
-rw-r--r--src/mongo/db/pipeline/plan_executor_pipeline.cpp18
-rw-r--r--src/mongo/db/pipeline/plan_executor_pipeline.h2
-rw-r--r--src/mongo/db/query/cqf_get_executor.cpp6
-rw-r--r--src/mongo/db/query/get_executor.cpp90
-rw-r--r--src/mongo/db/query/plan_executor.h24
-rw-r--r--src/mongo/db/query/plan_executor_factory.cpp9
-rw-r--r--src/mongo/db/query/plan_executor_factory.h3
-rw-r--r--src/mongo/db/query/plan_executor_impl.h4
-rw-r--r--src/mongo/db/query/plan_executor_sbe.cpp6
-rw-r--r--src/mongo/db/query/plan_executor_sbe.h11
-rw-r--r--src/mongo/db/stats/counters.h53
18 files changed, 264 insertions, 119 deletions
diff --git a/jstests/noPassthrough/query_engine_stats.js b/jstests/noPassthrough/query_engine_stats.js
index 0913e2f8596..aa5b9f4af22 100644
--- a/jstests/noPassthrough/query_engine_stats.js
+++ b/jstests/noPassthrough/query_engine_stats.js
@@ -63,18 +63,12 @@ function verifySlowQueryLog(db, expectedComment, queryFramework) {
// Ensure the profile filter contains the correct information about the queryFramework used.
function verifyProfiler(expectedComment, queryFramework) {
- const profileEntryFilter = {ns: "query_engine_stats.collection"};
+ const profileEntryFilter = {
+ ns: "query_engine_stats.collection",
+ "command.comment": expectedComment
+ };
const profileObj = getLatestProfilerEntry(db, profileEntryFilter);
- try {
- assert.eq(profileObj.command.comment, expectedComment);
- if (queryFramework) {
- assert.eq(profileObj.queryFramework, queryFramework);
- }
- } catch (e) {
- print('failed to find [{ "queryFramework" : "' + queryFramework + '", { "comment" : "' +
- expectedComment + '"} }] in the latest profiler entry.');
- throw (e);
- }
+ assert.eq(profileObj.queryFramework, queryFramework);
}
// Create an object with the correct queryFramework counter values after the specified type of
@@ -155,9 +149,25 @@ verifySlowQueryLog(db, queryComment, framework.find.classic);
compareQueryEngineCounters(expectedCounters);
verifyProfiler(queryComment, framework.find.classic);
+// Find with getMore.
+queryComment = "findClassicGetMore";
+let cursor = coll.find({a: {$gt: 2}}).comment(queryComment).batchSize(1);
+cursor.next(); // initial query
+verifyProfiler(queryComment, framework.find.classic);
+cursor.next(); // getMore performed
+verifyProfiler(queryComment, framework.find.classic);
+
+// Aggregation with getMore.
+queryComment = "aggClassicGetMore";
+cursor = coll.aggregate([{$match: {a: {$gt: 2}}}], {comment: queryComment, batchSize: 1});
+cursor.next(); // initial query
+verifyProfiler(queryComment, framework.find.classic);
+cursor.next(); // getMore performed
+verifyProfiler(queryComment, framework.find.classic);
+
// Turn SBE on.
assert.commandWorked(
- db.adminCommand({setParameter: 1, internalQueryFrameworkControl: "tryBonsai"}));
+ db.adminCommand({setParameter: 1, internalQueryFrameworkControl: "trySbeEngine"}));
// Run a find command.
expectedCounters = generateExpectedCounters(framework.find.sbe);
@@ -191,6 +201,28 @@ verifySlowQueryLog(db, queryComment, framework.find.sbe);
compareQueryEngineCounters(expectedCounters);
verifyProfiler(queryComment, framework.find.sbe);
+// SBE find with getMore.
+queryComment = "findSBEGetMore";
+cursor = coll.find({a: {$gt: 2}}).comment(queryComment).batchSize(1);
+cursor.next(); // initial query
+verifyProfiler(queryComment, framework.find.sbe);
+cursor.next(); // getMore performed
+verifyProfiler(queryComment, framework.find.sbe);
+
+// SBE aggregation with getMore.
+queryComment = "aggSBEGetMore";
+cursor = coll.aggregate(
+ [
+ {$_internalInhibitOptimization: {}},
+ {$group: {_id: "$a", acc: {$sum: "$b"}}},
+ {$match: {acc: {$gt: 0}}}
+ ],
+ {comment: queryComment, batchSize: 1});
+cursor.next(); // initial query
+verifyProfiler(queryComment, framework.find.sbe);
+cursor.next(); // getMore performed
+verifyProfiler(queryComment, framework.find.sbe);
+
MongoRunner.stopMongod(conn);
conn = MongoRunner.runMongod({restart: conn, setParameter: 'featureFlagCommonQueryFramework=1'});
@@ -228,5 +260,21 @@ verifySlowQueryLog(db, queryComment, framework.find.sbe);
compareQueryEngineCounters(expectedCounters);
verifyProfiler(queryComment, framework.find.sbe);
+// CQF find with getMore.
+queryComment = "findCQFGetMore";
+cursor = coll.find({a: {$gt: 2}}).comment(queryComment).batchSize(1);
+cursor.next(); // initial query
+verifyProfiler(queryComment, "cqf");
+cursor.next(); // getMore performed
+verifyProfiler(queryComment, "cqf");
+
+// CQF aggregation with getMore.
+queryComment = "aggCQFGetMore";
+cursor = coll.aggregate([{$match: {a: {$gt: 2}}}], {comment: queryComment, batchSize: 1});
+cursor.next(); // initial query
+verifyProfiler(queryComment, "cqf");
+cursor.next(); // getMore performed
+verifyProfiler(queryComment, "cqf");
+
MongoRunner.stopMongod(conn);
})();
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp
index 09aa73cc315..02b5739397c 100644
--- a/src/mongo/db/commands/getmore_cmd.cpp
+++ b/src/mongo/db/commands/getmore_cmd.cpp
@@ -572,6 +572,8 @@ public:
curOp->setOriginatingCommand_inlock(originatingCommand);
}
+ curOp->debug().queryFramework = exec->getQueryFramework();
+
// Update the genericCursor stored in curOp with the new cursor stats.
curOp->setGenericCursor_inlock(cursorPin->toGenericCursor());
}
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index efca157417f..5a4b171d0b9 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -608,9 +608,6 @@ std::vector<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> createLegacyEx
request,
hasGeoNearStage,
liteParsedPipeline.hasChangeStream())) {
- // Mark that this query does not use DocumentSource.
- curOp->debug().documentSourceUsed = false;
-
// This pipeline is currently empty, but once completed it will have only one source,
// which is a DocumentSourceCursor. Instead of creating a whole pipeline to do nothing
// more than forward the results of its cursor document source, we can use the
@@ -618,9 +615,6 @@ std::vector<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> createLegacyEx
// have gotten from find command.
execs.emplace_back(std::move(attachExecutorCallback.second));
} else {
- // Mark that this query uses DocumentSource.
- curOp->debug().documentSourceUsed = true;
-
getSearchHelpers(expCtx->opCtx->getServiceContext())
->injectSearchShardFiltererIfNeeded(pipeline.get());
@@ -1017,6 +1011,7 @@ Status runAggregate(OperationContext* opCtx,
auto planSummary = execs[0]->getPlanExplainer().getPlanSummary();
stdx::lock_guard<Client> lk(*opCtx->getClient());
curOp->setPlanSummary_inlock(std::move(planSummary));
+ curOp->debug().queryFramework = execs[0]->getQueryFramework();
}
}
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index 9e6b89c1af4..ea2e0456911 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -894,10 +894,20 @@ void OpDebug::report(OperationContext* opCtx,
pAttrs->addDeepCopy("planCacheKey", zeroPaddedHex(*planCacheKey));
}
- if (classicEngineUsed) {
- pAttrs->add("queryFramework", classicEngineUsed.value() ? "classic" : "sbe");
- } else if (cqfUsed) {
- pAttrs->add("queryFramework", "cqf");
+ switch (queryFramework) {
+ case PlanExecutor::QueryFramework::kClassicOnly:
+ case PlanExecutor::QueryFramework::kClassicHybrid:
+ pAttrs->add("queryFramework", "classic");
+ break;
+ case PlanExecutor::QueryFramework::kSBEOnly:
+ case PlanExecutor::QueryFramework::kSBEHybrid:
+ pAttrs->add("queryFramework", "sbe");
+ break;
+ case PlanExecutor::QueryFramework::kCQF:
+ pAttrs->add("queryFramework", "cqf");
+ break;
+ case PlanExecutor::QueryFramework::kUnknown:
+ break;
}
if (!errInfo.isOK()) {
@@ -1070,10 +1080,20 @@ void OpDebug::append(OperationContext* opCtx,
b.append("planCacheKey", zeroPaddedHex(*planCacheKey));
}
- if (classicEngineUsed) {
- b.append("queryFramework", classicEngineUsed.value() ? "classic" : "sbe");
- } else if (cqfUsed) {
- b.append("queryFramework", "cqf");
+ switch (queryFramework) {
+ case PlanExecutor::QueryFramework::kClassicOnly:
+ case PlanExecutor::QueryFramework::kClassicHybrid:
+ b.append("queryFramework", "classic");
+ break;
+ case PlanExecutor::QueryFramework::kSBEOnly:
+ case PlanExecutor::QueryFramework::kSBEHybrid:
+ b.append("queryFramework", "sbe");
+ break;
+ case PlanExecutor::QueryFramework::kCQF:
+ b.append("queryFramework", "cqf");
+ break;
+ case PlanExecutor::QueryFramework::kUnknown:
+ break;
}
{
@@ -1341,10 +1361,20 @@ std::function<BSONObj(ProfileFilter::Args)> OpDebug::appendStaged(StringSet requ
});
addIfNeeded("queryFramework", [](auto field, auto args, auto& b) {
- if (args.op.classicEngineUsed) {
- b.append("queryFramework", args.op.classicEngineUsed.value() ? "classic" : "sbe");
- } else if (args.op.cqfUsed) {
- b.append("queryFramework", "cqf");
+ switch (args.op.queryFramework) {
+ case PlanExecutor::QueryFramework::kClassicOnly:
+ case PlanExecutor::QueryFramework::kClassicHybrid:
+ b.append("queryFramework", "classic");
+ break;
+ case PlanExecutor::QueryFramework::kSBEOnly:
+ case PlanExecutor::QueryFramework::kSBEHybrid:
+ b.append("queryFramework", "sbe");
+ break;
+ case PlanExecutor::QueryFramework::kCQF:
+ b.append("queryFramework", "cqf");
+ break;
+ case PlanExecutor::QueryFramework::kUnknown:
+ break;
}
});
diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h
index 9f83bd92cf1..c4cfcbcbb42 100644
--- a/src/mongo/db/curop.h
+++ b/src/mongo/db/curop.h
@@ -266,17 +266,8 @@ public:
// The hash of the query's "stable" key. This represents the query's shape.
boost::optional<uint32_t> queryHash;
- // Has a value if this operation is a query. True if the execution tree for the find part of the
- // query was built exclusively using the classic query engine, false if any part was built using
- // SBE.
- boost::optional<bool> classicEngineUsed;
-
- // Has a value if this operation is an aggregation query. True if `DocumentSources` were
- // involved in the execution tree for this query, false if they were not.
- boost::optional<bool> documentSourceUsed;
-
- // Indicates whether this operation used the common query framework (CQF).
- bool cqfUsed{false};
+ // The query framework that this operation used. Will be unknown for non query operations.
+ PlanExecutor::QueryFramework queryFramework{PlanExecutor::QueryFramework::kUnknown};
// Tracks the amount of indexed loop joins in a pushed down lookup stage.
int indexedLoopJoin{0};
diff --git a/src/mongo/db/pipeline/document_source_cursor.cpp b/src/mongo/db/pipeline/document_source_cursor.cpp
index 81e023837b9..6c59043a7ed 100644
--- a/src/mongo/db/pipeline/document_source_cursor.cpp
+++ b/src/mongo/db/pipeline/document_source_cursor.cpp
@@ -315,7 +315,8 @@ DocumentSourceCursor::DocumentSourceCursor(
: DocumentSource(kStageName, pCtx),
_currentBatch(cursorType),
_exec(std::move(exec)),
- _trackOplogTS(trackOplogTimestamp) {
+ _trackOplogTS(trackOplogTimestamp),
+ _queryFramework(_exec->getQueryFramework()) {
// It is illegal for both 'kEmptyDocuments' and 'trackOplogTimestamp' to be set.
invariant(!(cursorType == CursorType::kEmptyDocuments && trackOplogTimestamp));
diff --git a/src/mongo/db/pipeline/document_source_cursor.h b/src/mongo/db/pipeline/document_source_cursor.h
index d3f61cbee7d..0fd9428069a 100644
--- a/src/mongo/db/pipeline/document_source_cursor.h
+++ b/src/mongo/db/pipeline/document_source_cursor.h
@@ -126,6 +126,10 @@ public:
return _exec->getPlanExplainer().getVersion();
}
+ PlanExecutor::QueryFramework getQueryFramework() const {
+ return _queryFramework;
+ }
+
BSONObj serializeToBSONForDebug() const final {
// Feel free to add any useful information here. For now this has not been useful for
// debugging so is left empty.
@@ -271,6 +275,8 @@ private:
// Specific stats for $cursor stage.
DocumentSourceCursorStats _stats;
+
+ PlanExecutor::QueryFramework _queryFramework;
};
} // namespace mongo
diff --git a/src/mongo/db/pipeline/plan_executor_pipeline.cpp b/src/mongo/db/pipeline/plan_executor_pipeline.cpp
index 0b26a7db813..a41b1b1ab73 100644
--- a/src/mongo/db/pipeline/plan_executor_pipeline.cpp
+++ b/src/mongo/db/pipeline/plan_executor_pipeline.cpp
@@ -252,4 +252,22 @@ void PlanExecutorPipeline::markAsKilled(Status killStatus) {
}
}
+PlanExecutor::QueryFramework PlanExecutorPipeline::getQueryFramework() const {
+ // If this executor has a $cursor source at the front, use the query framework of that executor
+ // backing the cursor stage in order to determine whether the current pipeline is a hybrid plan.
+ if (auto cursor = dynamic_cast<DocumentSourceCursor*>(_pipeline->peekFront())) {
+ switch (cursor->getQueryFramework()) {
+ case PlanExecutor::QueryFramework::kClassicOnly:
+ return PlanExecutor::QueryFramework::kClassicHybrid;
+ case PlanExecutor::QueryFramework::kSBEOnly:
+ return PlanExecutor::QueryFramework::kSBEHybrid;
+ default:
+ MONGO_UNREACHABLE_TASSERT(6884701);
+ }
+ }
+ // If this executor doesn't have a $cursor source, then return classicOnly as it cannot be a
+ // hybrid plan.
+ return PlanExecutor::QueryFramework::kClassicOnly;
+}
+
} // namespace mongo
diff --git a/src/mongo/db/pipeline/plan_executor_pipeline.h b/src/mongo/db/pipeline/plan_executor_pipeline.h
index ce0b840f3c9..cfc1a9052c3 100644
--- a/src/mongo/db/pipeline/plan_executor_pipeline.h
+++ b/src/mongo/db/pipeline/plan_executor_pipeline.h
@@ -171,6 +171,8 @@ public:
return _pipeline->getTypeString();
}
+ PlanExecutor::QueryFramework getQueryFramework() const override final;
+
private:
/**
* Obtains the next document from the underlying Pipeline, and does change streams-related
diff --git a/src/mongo/db/query/cqf_get_executor.cpp b/src/mongo/db/query/cqf_get_executor.cpp
index cc1c42bc3af..0ca8ef9405a 100644
--- a/src/mongo/db/query/cqf_get_executor.cpp
+++ b/src/mongo/db/query/cqf_get_executor.cpp
@@ -364,7 +364,8 @@ static std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> optimizeAndCreateExe
QueryPlannerParams::Options::DEFAULT,
nss,
std::move(yieldPolicy),
- false /*isFromPlanCache*/));
+ false /*isFromPlanCache*/,
+ true /* generatedByBonsai */));
return planExec;
}
@@ -657,9 +658,6 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
validateCommandOptions(canonicalQuery.get(), collection, indexHint, involvedCollections);
- auto curOp = CurOp::get(opCtx);
- curOp->debug().cqfUsed = true;
-
const bool requireRID = canonicalQuery ? canonicalQuery->getForceGenerateRecordId() : false;
const bool collectionExists = collection != nullptr;
const std::string uuidStr = collectionExists ? collection->uuid().toString() : "<missing_uuid>";
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index 2325df018e2..d36bb993a33 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -1171,11 +1171,6 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getClassicExecu
std::unique_ptr<CanonicalQuery> canonicalQuery,
PlanYieldPolicy::YieldPolicy yieldPolicy,
const QueryPlannerParams& plannerParams) {
- // Mark that this query uses the classic engine, unless this has already been set.
- OpDebug& opDebug = CurOp::get(opCtx)->debug();
- if (!opDebug.classicEngineUsed) {
- opDebug.classicEngineUsed = true;
- }
auto ws = std::make_unique<WorkingSet>();
ClassicPrepareExecutionHelper helper{
opCtx, collection, ws.get(), canonicalQuery.get(), nullptr, plannerParams};
@@ -1283,9 +1278,6 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getSlotBasedExe
// Now that we know what executor we are going to use, fill in some opDebug information, unless
// it has already been filled by an outer pipeline.
OpDebug& opDebug = CurOp::get(opCtx)->debug();
- if (!opDebug.classicEngineUsed) {
- opDebug.classicEngineUsed = false;
- }
const auto& mainColl = collections.getMainCollection();
if (mainColl) {
auto planCacheKey = plan_cache_key_factory::make(*cq, collections);
@@ -1375,7 +1367,8 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getSlotBasedExe
plannerParams.options,
std::move(nss),
std::move(yieldPolicy),
- planningResult->isRecoveredFromPlanCache());
+ planningResult->isRecoveredFromPlanCache(),
+ false /* generatedByBonsai */);
}
/**
@@ -1472,43 +1465,52 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutor(
std::function<void(CanonicalQuery*, bool)> extractAndAttachPipelineStages,
PlanYieldPolicy::YieldPolicy yieldPolicy,
const QueryPlannerParams& plannerParams) {
- invariant(canonicalQuery);
- const auto& mainColl = collections.getMainCollection();
- canonicalQuery->setSbeCompatible(
- isQuerySbeCompatible(&mainColl, canonicalQuery.get(), plannerParams.options));
-
- if (isEligibleForBonsai(*canonicalQuery, opCtx, mainColl)) {
- return getSBEExecutorViaCascadesOptimizer(mainColl, std::move(canonicalQuery));
- }
-
- // Use SBE if 'canonicalQuery' is SBE compatible.
- if (!canonicalQuery->getForceClassicEngine() && canonicalQuery->isSbeCompatible()) {
- auto statusWithExecutor = attemptToGetSlotBasedExecutor(opCtx,
- collections,
- std::move(canonicalQuery),
- extractAndAttachPipelineStages,
- yieldPolicy,
- plannerParams);
- if (!statusWithExecutor.isOK()) {
- return statusWithExecutor.getStatus();
- }
- auto& maybeExecutor = statusWithExecutor.getValue();
- if (stdx::holds_alternative<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>>(
- maybeExecutor)) {
- return std::move(
- stdx::get<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>>(maybeExecutor));
- } else {
- // The query is not eligible for SBE execution - reclaim the canonical query and fall
- // back to classic.
- tassert(7087103,
- "return value must contain canonical query if not executor",
- stdx::holds_alternative<std::unique_ptr<CanonicalQuery>>(maybeExecutor));
- canonicalQuery = std::move(stdx::get<std::unique_ptr<CanonicalQuery>>(maybeExecutor));
+ auto exec = [&]() {
+ invariant(canonicalQuery);
+ const auto& mainColl = collections.getMainCollection();
+ canonicalQuery->setSbeCompatible(
+ isQuerySbeCompatible(&mainColl, canonicalQuery.get(), plannerParams.options));
+
+ if (isEligibleForBonsai(*canonicalQuery, opCtx, mainColl)) {
+ return StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>>(
+ getSBEExecutorViaCascadesOptimizer(mainColl, std::move(canonicalQuery)));
+ }
+
+ // Use SBE if 'canonicalQuery' is SBE compatible.
+ if (!canonicalQuery->getForceClassicEngine() && canonicalQuery->isSbeCompatible()) {
+ auto statusWithExecutor = attemptToGetSlotBasedExecutor(opCtx,
+ collections,
+ std::move(canonicalQuery),
+ extractAndAttachPipelineStages,
+ yieldPolicy,
+ plannerParams);
+ if (!statusWithExecutor.isOK()) {
+ return StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>>(
+ statusWithExecutor.getStatus());
+ }
+ auto& maybeExecutor = statusWithExecutor.getValue();
+ if (stdx::holds_alternative<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>>(
+ maybeExecutor)) {
+ return StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>>(
+ std::move(stdx::get<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>>(
+ maybeExecutor)));
+ } else {
+ // The query is not eligible for SBE execution - reclaim the canonical query and
+ // fall back to classic.
+ tassert(7087103,
+ "return value must contain canonical query if not executor",
+ stdx::holds_alternative<std::unique_ptr<CanonicalQuery>>(maybeExecutor));
+ canonicalQuery =
+ std::move(stdx::get<std::unique_ptr<CanonicalQuery>>(maybeExecutor));
+ }
}
+ return getClassicExecutor(
+ opCtx, mainColl, std::move(canonicalQuery), yieldPolicy, plannerParams);
+ }();
+ if (exec.isOK()) {
+ CurOp::get(opCtx)->debug().queryFramework = exec.getValue()->getQueryFramework();
}
-
- return getClassicExecutor(
- opCtx, mainColl, std::move(canonicalQuery), yieldPolicy, plannerParams);
+ return exec;
}
StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutor(
diff --git a/src/mongo/db/query/plan_executor.h b/src/mongo/db/query/plan_executor.h
index a94e87648dd..f47f0e1a57b 100644
--- a/src/mongo/db/query/plan_executor.h
+++ b/src/mongo/db/query/plan_executor.h
@@ -384,6 +384,30 @@ public:
virtual boost::optional<StringData> getExecutorType() const {
return boost::none;
}
+
+ /**
+ * Describes the query framework which an executor used.
+ */
+ enum class QueryFramework {
+ // Null value.
+ kUnknown,
+ // The entirety of this plan was executed in the classic execution engine.
+ kClassicOnly,
+ // This plan was executed using classic document source and any find pushdown was executed
+ // in the classic execution engine.
+ kClassicHybrid,
+ // The entirety of this plan was exectued in SBE via stage builders.
+ kSBEOnly,
+ // A portion of this plan was executed in SBE via stage builders.
+ kSBEHybrid,
+ // The entirely of this plan was executed using CQF. Hybrid CQF plans are not possible.
+ kCQF
+ };
+
+ /**
+ * Returns the query framework that this executor used.
+ */
+ virtual QueryFramework getQueryFramework() const = 0;
};
} // namespace mongo
diff --git a/src/mongo/db/query/plan_executor_factory.cpp b/src/mongo/db/query/plan_executor_factory.cpp
index 1ff8d5660b8..fc2107e8164 100644
--- a/src/mongo/db/query/plan_executor_factory.cpp
+++ b/src/mongo/db/query/plan_executor_factory.cpp
@@ -130,7 +130,8 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> make(
size_t plannerOptions,
NamespaceString nss,
std::unique_ptr<PlanYieldPolicySBE> yieldPolicy,
- bool planIsFromCache) {
+ bool planIsFromCache,
+ bool generatedByBonsai) {
auto&& [rootStage, data] = root;
LOGV2_DEBUG(4822860,
@@ -153,7 +154,8 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> make(
plannerOptions & QueryPlannerParams::RETURN_OWNED_DATA,
std::move(nss),
false,
- std::move(yieldPolicy)),
+ std::move(yieldPolicy),
+ generatedByBonsai),
PlanExecutor::Deleter{opCtx}}};
}
@@ -179,7 +181,8 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> make(
plannerOptions & QueryPlannerParams::RETURN_OWNED_DATA,
std::move(nss),
true,
- std::move(yieldPolicy)),
+ std::move(yieldPolicy),
+ false),
PlanExecutor::Deleter{opCtx}}};
}
diff --git a/src/mongo/db/query/plan_executor_factory.h b/src/mongo/db/query/plan_executor_factory.h
index be7ab263a88..da12d682392 100644
--- a/src/mongo/db/query/plan_executor_factory.h
+++ b/src/mongo/db/query/plan_executor_factory.h
@@ -119,7 +119,8 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> make(
size_t plannerOptions,
NamespaceString nss,
std::unique_ptr<PlanYieldPolicySBE> yieldPolicy,
- bool isFromPlanCache);
+ bool isFromPlanCache,
+ bool generatedByBonsai);
/**
* Similar to the factory function above in that it also constructs an executor for the winning SBE
diff --git a/src/mongo/db/query/plan_executor_impl.h b/src/mongo/db/query/plan_executor_impl.h
index 5f6563bf7e4..6855e6b14e8 100644
--- a/src/mongo/db/query/plan_executor_impl.h
+++ b/src/mongo/db/query/plan_executor_impl.h
@@ -127,6 +127,10 @@ public:
LockPolicy lockPolicy() const final;
const PlanExplainer& getPlanExplainer() const final;
+ PlanExecutor::QueryFramework getQueryFramework() const override final {
+ return PlanExecutor::QueryFramework::kClassicOnly;
+ }
+
/**
* Same as restoreState() but without the logic to retry if a WriteConflictException is thrown.
*
diff --git a/src/mongo/db/query/plan_executor_sbe.cpp b/src/mongo/db/query/plan_executor_sbe.cpp
index bd62138da46..6327a6b8a10 100644
--- a/src/mongo/db/query/plan_executor_sbe.cpp
+++ b/src/mongo/db/query/plan_executor_sbe.cpp
@@ -54,7 +54,8 @@ PlanExecutorSBE::PlanExecutorSBE(OperationContext* opCtx,
bool returnOwnedBson,
NamespaceString nss,
bool isOpen,
- std::unique_ptr<PlanYieldPolicySBE> yieldPolicy)
+ std::unique_ptr<PlanYieldPolicySBE> yieldPolicy,
+ bool generatedByBonsai)
: _state{isOpen ? State::kOpened : State::kClosed},
_opCtx(opCtx),
_nss(std::move(nss)),
@@ -64,7 +65,8 @@ PlanExecutorSBE::PlanExecutorSBE(OperationContext* opCtx,
_solution{std::move(candidates.winner().solution)},
_stash{std::move(candidates.winner().results)},
_cq{std::move(cq)},
- _yieldPolicy(std::move(yieldPolicy)) {
+ _yieldPolicy(std::move(yieldPolicy)),
+ _generatedByBonsai(generatedByBonsai) {
invariant(!_nss.isEmpty());
invariant(_root);
diff --git a/src/mongo/db/query/plan_executor_sbe.h b/src/mongo/db/query/plan_executor_sbe.h
index 71b894c9f60..1327dd2b455 100644
--- a/src/mongo/db/query/plan_executor_sbe.h
+++ b/src/mongo/db/query/plan_executor_sbe.h
@@ -50,7 +50,8 @@ public:
bool returnOwnedBson,
NamespaceString nss,
bool isOpen,
- std::unique_ptr<PlanYieldPolicySBE> yieldPolicy);
+ std::unique_ptr<PlanYieldPolicySBE> yieldPolicy,
+ bool generatedByBonsai);
CanonicalQuery* getCanonicalQuery() const override {
return _cq.get();
@@ -147,6 +148,11 @@ public:
return _isSaveRecoveryUnitAcrossCommandsEnabled;
}
+ PlanExecutor::QueryFramework getQueryFramework() const override final {
+ return _generatedByBonsai ? PlanExecutor::QueryFramework::kCQF
+ : PlanExecutor::QueryFramework::kSBEOnly;
+ }
+
private:
template <typename ObjectType>
ExecState getNextImpl(ObjectType* out, RecordId* dlOut);
@@ -205,6 +211,9 @@ private:
bool _isDisposed{false};
bool _isSaveRecoveryUnitAcrossCommandsEnabled = false;
+
+ // Indicates whether this executor was constructed via Bonsai/CQF.
+ bool _generatedByBonsai{false};
};
/**
diff --git a/src/mongo/db/stats/counters.h b/src/mongo/db/stats/counters.h
index 65b15958c59..d066202d375 100644
--- a/src/mongo/db/stats/counters.h
+++ b/src/mongo/db/stats/counters.h
@@ -323,31 +323,40 @@ public:
auto& debug = curop->debug();
const BSONObj& cmdObj = curop->opDescription();
auto cmdName = cmdObj.firstElementFieldNameStringData();
- if (cmdName == "find" && debug.classicEngineUsed) {
- if (debug.classicEngineUsed.get()) {
- classicFindQueryCounter.increment();
- } else {
- sbeFindQueryCounter.increment();
+
+ if (cmdName == "find") {
+ switch (debug.queryFramework) {
+ case PlanExecutor::QueryFramework::kClassicOnly:
+ classicFindQueryCounter.increment();
+ break;
+ case PlanExecutor::QueryFramework::kSBEOnly:
+ sbeFindQueryCounter.increment();
+ break;
+ case PlanExecutor::QueryFramework::kCQF:
+ cqfFindQueryCounter.increment();
+ break;
+ default:
+ break;
}
- } else if (cmdName == "aggregate" && debug.classicEngineUsed && debug.documentSourceUsed) {
- if (debug.classicEngineUsed.get()) {
- if (debug.documentSourceUsed.get()) {
- classicHybridAggregationCounter.increment();
- } else {
+ } else if (cmdName == "aggregate") {
+ switch (debug.queryFramework) {
+ case PlanExecutor::QueryFramework::kClassicOnly:
classicOnlyAggregationCounter.increment();
- }
- } else {
- if (debug.documentSourceUsed.get()) {
- sbeHybridAggregationCounter.increment();
- } else {
+ break;
+ case PlanExecutor::QueryFramework::kClassicHybrid:
+ classicHybridAggregationCounter.increment();
+ break;
+ case PlanExecutor::QueryFramework::kSBEOnly:
sbeOnlyAggregationCounter.increment();
- }
- }
- } else if (debug.cqfUsed) {
- if (cmdName == "find") {
- cqfFindQueryCounter.increment();
- } else {
- cqfAggregationQueryCounter.increment();
+ break;
+ case PlanExecutor::QueryFramework::kSBEHybrid:
+ sbeHybridAggregationCounter.increment();
+ break;
+ case PlanExecutor::QueryFramework::kCQF:
+ cqfAggregationQueryCounter.increment();
+ break;
+ case PlanExecutor::QueryFramework::kUnknown:
+ break;
}
}
}