diff options
-rw-r--r-- | buildscripts/resmokeconfig/suites/cqf_passthrough.yml | 1 | ||||
-rw-r--r-- | jstests/core/sbe/from_plan_cache_flag.js | 46 | ||||
-rw-r--r-- | jstests/noPassthrough/plan_cache_group_lookup.js | 34 | ||||
-rw-r--r-- | src/mongo/db/curop.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/curop.h | 1 | ||||
-rw-r--r-- | src/mongo/db/query/cqf_get_executor.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/query/get_executor.cpp | 25 | ||||
-rw-r--r-- | src/mongo/db/query/plan_cache.h | 6 | ||||
-rw-r--r-- | src/mongo/db/query/plan_executor_factory.cpp | 30 | ||||
-rw-r--r-- | src/mongo/db/query/plan_executor_factory.h | 3 | ||||
-rw-r--r-- | src/mongo/db/query/plan_executor_sbe.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/query/plan_explainer_factory.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/query/plan_explainer_factory.h | 1 | ||||
-rw-r--r-- | src/mongo/db/query/plan_explainer_impl.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/query/plan_explainer_sbe.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/query/plan_explainer_sbe.h | 7 | ||||
-rw-r--r-- | src/mongo/db/query/plan_ranker.h | 2 | ||||
-rw-r--r-- | src/mongo/db/query/plan_summary_stats.h | 2 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_cached_solution_planner.cpp | 10 |
19 files changed, 155 insertions, 36 deletions
diff --git a/buildscripts/resmokeconfig/suites/cqf_passthrough.yml b/buildscripts/resmokeconfig/suites/cqf_passthrough.yml index 90a35ae3547..9ae9f4dd69a 100644 --- a/buildscripts/resmokeconfig/suites/cqf_passthrough.yml +++ b/buildscripts/resmokeconfig/suites/cqf_passthrough.yml @@ -35,6 +35,7 @@ selector: - jstests/core/plan_cache_shell_helpers.js - jstests/core/plan_cache_stats_shard_and_host.js - jstests/core/profile_query_hash.js + - jstests/core/sbe/from_plan_cache_flag.js - jstests/core/sbe/plan_cache_sbe_with_or_queries.js - jstests/core/sbe_plan_cache_autoparameterize_collscan.js # TODO SERVER-62034 Prevent distinct() from using CQF. diff --git a/jstests/core/sbe/from_plan_cache_flag.js b/jstests/core/sbe/from_plan_cache_flag.js new file mode 100644 index 00000000000..006b76d5af5 --- /dev/null +++ b/jstests/core/sbe/from_plan_cache_flag.js @@ -0,0 +1,46 @@ +// @tags: [ +// requires_profiling, +// does_not_support_stepdowns +// ] +(function() { +"use strict"; + +load("jstests/libs/analyze_plan.js"); +load("jstests/libs/sbe_util.js"); // For checkSBEEnabled. +load("jstests/libs/profiler.js"); // For getLatestProfilerEntry. +load("jstests/libs/sbe_util.js"); // For checkSBEEnabled. + +if (!checkSBEEnabled(db, ["featureFlagSbeFull"], true /* checkAllNodes */)) { + jsTest.log("Skip running the test because SBE is not enabled"); + return; +} +var testDB = db.getSiblingDB("profile_findandmodify"); +assert.commandWorked(testDB.dropDatabase()); +var coll = testDB.getCollection("test"); +assert.commandWorked(testDB.setProfilingLevel(2)); +coll.drop(); +coll.getPlanCache().clear(); + +assert.commandWorked(coll.insert({a: 1})); +assert.commandWorked(coll.insert({a: 2})); +assert.commandWorked(coll.insert({a: 3})); +assert.commandWorked(coll.insert({a: 2})); + +let pipeline = {$match: {a: 1}}; +coll.aggregate([pipeline]).toArray(); +let profileObj = getLatestProfilerEntry(testDB); +/* fromPlanCache can be undefined in the profiler entry. The first ! determines the + * profileObj.fromPlanCache value's associated true/false value (important in the case where + * undefined) and then returns the opposite of the associated true/false value. The second ! + * returns the opposite of the opposite value. In other words, the !! returns the boolean true/false + * association of a value. */ +assert.eq(!!profileObj.fromPlanCache, false); + +coll.aggregate({$match: {a: 2}}).toArray(); +profileObj = getLatestProfilerEntry(testDB); +assert.eq(!!profileObj.fromPlanCache, true); + +coll.aggregate({$match: {a: 3}}).toArray(); +profileObj = getLatestProfilerEntry(testDB); +assert.eq(!!profileObj.fromPlanCache, true); +}());
\ No newline at end of file diff --git a/jstests/noPassthrough/plan_cache_group_lookup.js b/jstests/noPassthrough/plan_cache_group_lookup.js index 0c20d16e2c2..7fc63a5094b 100644 --- a/jstests/noPassthrough/plan_cache_group_lookup.js +++ b/jstests/noPassthrough/plan_cache_group_lookup.js @@ -36,12 +36,15 @@ assert.commandWorked(db.setProfilingLevel(2)); * Assert that the last aggregation command has a corresponding plan cache entry with the desired * properties. 'version' is 1 if it's classic cache, 2 if it's SBE cache. 'isActive' is true if the * cache entry is active. 'fromMultiPlanner' is true if the query part of aggregation has been - * multi-planned. 'forcesClassicEngine' is true if the query is forced to use classic engine. + * multi-planned. 'fromPlanCache' is true if the winning plan was retrieved from the plan cache. + * 'forcesClassicEngine' is true if the query is forced to use classic engine. */ -function assertCacheUsage({version, fromMultiPlanner, isActive, forcesClassicEngine = false}) { +function assertCacheUsage( + {version, fromMultiPlanner, fromPlanCache, isActive, forcesClassicEngine = false}) { const profileObj = getLatestProfilerEntry( db, {op: "command", "command.pipeline": {$exists: true}, ns: coll.getFullName()}); assert.eq(fromMultiPlanner, !!profileObj.fromMultiPlanner, profileObj); + assert.eq(fromPlanCache, !!profileObj.fromPlanCache, profileObj); const entries = coll.getPlanCache().list(); assert.eq(entries.length, 1, entries); @@ -71,19 +74,34 @@ function assertCacheUsage({version, fromMultiPlanner, isActive, forcesClassicEng function testLoweredPipeline({pipeline, version, forcesClassicEngine = false}) { let results = coll.aggregate(pipeline).toArray(); assert.eq(results.length, 1, results); - const entry = assertCacheUsage( - {version: version, fromMultiPlanner: true, isActive: false, forcesClassicEngine}); + const entry = assertCacheUsage({ + version: version, + fromMultiPlanner: true, + fromPlanCache: false, + isActive: false, + forcesClassicEngine + }); results = coll.aggregate(pipeline).toArray(); assert.eq(results.length, 1, results); - let nextEntry = assertCacheUsage( - {version: version, fromMultiPlanner: true, isActive: true, forcesClassicEngine}); + let nextEntry = assertCacheUsage({ + version: version, + fromMultiPlanner: true, + fromPlanCache: false, + isActive: true, + forcesClassicEngine + }); assert.eq(entry.planCacheKey, nextEntry.planCacheKey, {entry, nextEntry}); results = coll.aggregate(pipeline).toArray(); assert.eq(results.length, 1, results); - nextEntry = assertCacheUsage( - {version: version, fromMultiPlanner: false, isActive: true, forcesClassicEngine}); + nextEntry = assertCacheUsage({ + version: version, + fromMultiPlanner: false, + fromPlanCache: true, + isActive: true, + forcesClassicEngine + }); assert.eq(entry.planCacheKey, nextEntry.planCacheKey, {entry, nextEntry}); return nextEntry; diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp index 9c362854d97..a01bb33437f 100644 --- a/src/mongo/db/curop.cpp +++ b/src/mongo/db/curop.cpp @@ -831,6 +831,7 @@ void OpDebug::report(OperationContext* opCtx, OPDEBUG_TOATTR_HELP_BOOL(hasSortStage); OPDEBUG_TOATTR_HELP_BOOL(usedDisk); OPDEBUG_TOATTR_HELP_BOOL(fromMultiPlanner); + OPDEBUG_TOATTR_HELP_BOOL(fromPlanCache); if (replanReason) { bool replanned = true; OPDEBUG_TOATTR_HELP_BOOL(replanned); @@ -999,6 +1000,7 @@ void OpDebug::append(OperationContext* opCtx, OPDEBUG_APPEND_BOOL(b, hasSortStage); OPDEBUG_APPEND_BOOL(b, usedDisk); OPDEBUG_APPEND_BOOL(b, fromMultiPlanner); + OPDEBUG_APPEND_BOOL(b, fromPlanCache); if (replanReason) { bool replanned = true; OPDEBUG_APPEND_BOOL(b, replanned); @@ -1228,6 +1230,9 @@ std::function<BSONObj(ProfileFilter::Args)> OpDebug::appendStaged(StringSet requ addIfNeeded("fromMultiPlanner", [](auto field, auto args, auto& b) { OPDEBUG_APPEND_BOOL2(b, field, args.op.fromMultiPlanner); }); + addIfNeeded("fromPlanCache", [](auto field, auto args, auto& b) { + OPDEBUG_APPEND_BOOL2(b, field, args.op.fromPlanCache); + }); addIfNeeded("replanned", [](auto field, auto args, auto& b) { if (args.op.replanReason) { OPDEBUG_APPEND_BOOL2(b, field, true); @@ -1443,6 +1448,7 @@ void OpDebug::setPlanSummaryMetrics(const PlanSummaryStats& planSummaryStats) { sortTotalDataSizeBytes = planSummaryStats.sortTotalDataSizeBytes; keysSorted = planSummaryStats.keysSorted; fromMultiPlanner = planSummaryStats.fromMultiPlanner; + fromPlanCache = planSummaryStats.fromPlanCache; replanReason = planSummaryStats.replanReason; } diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h index d0e5d13313d..29a63981b68 100644 --- a/src/mongo/db/curop.h +++ b/src/mongo/db/curop.h @@ -251,6 +251,7 @@ public: // single solution). bool fromMultiPlanner{false}; + bool fromPlanCache{false}; // True if a replan was triggered during the execution of this operation. boost::optional<std::string> replanReason; diff --git a/src/mongo/db/query/cqf_get_executor.cpp b/src/mongo/db/query/cqf_get_executor.cpp index 3143822a986..355146d85ce 100644 --- a/src/mongo/db/query/cqf_get_executor.cpp +++ b/src/mongo/db/query/cqf_get_executor.cpp @@ -342,7 +342,8 @@ static std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> optimizeAndCreateExe MultipleCollectionAccessor(collection), QueryPlannerParams::Options::DEFAULT, nss, - std::move(yieldPolicy))); + std::move(yieldPolicy), + false /*isFromPlanCache*/)); return planExec; } diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index 3e8653eb229..cb1113404a2 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -521,10 +521,17 @@ public: std::tuple<std::unique_ptr<PlanStage>, std::unique_ptr<QuerySolution>> extractResultData() { return std::make_tuple(std::move(_root), std::move(_solution)); } + void setRecoveredFromPlanCache(bool val) { + _fromPlanCache = val; + } + bool isRecoveredFromPlanCache() { + return _fromPlanCache; + } private: std::unique_ptr<PlanStage> _root; std::unique_ptr<QuerySolution> _solution; + bool _fromPlanCache{false}; }; /** @@ -601,12 +608,21 @@ public: _recoveredPinnedCacheEntry = pinnedEntry; } + void setRecoveredFromPlanCache(bool val) { + _fromPlanCache = val; + } + + bool isRecoveredFromPlanCache() { + return _fromPlanCache; + } + private: QuerySolutionVector _solutions; PlanStageVector _roots; boost::optional<size_t> _decisionWorks; bool _needSubplanning{false}; bool _recoveredPinnedCacheEntry{false}; + bool _fromPlanCache{false}; }; /** @@ -1156,6 +1172,7 @@ protected: result->setDecisionWorks(cacheEntry->decisionWorks); result->setRecoveredPinnedCacheEntry(cacheEntry->isPinned()); result->emplace(std::make_pair(std::move(root), std::move(stageData))); + result->setRecoveredFromPlanCache(true); return result; } } @@ -1197,7 +1214,7 @@ protected: result->emplace(std::move(execTree), std::move(querySolution)); result->setDecisionWorks(cs->decisionWorks); - + result->setRecoveredFromPlanCache(true); return result; } } @@ -1308,7 +1325,7 @@ std::unique_ptr<sbe::RuntimePlanner> makeRuntimePlannerIfNeeded( invariant(numSolutions == 1); // If we have a single solution and the plan is not pinned or plan contains a hash_lookup stage, - // we will need we will need to do the runtime planning to check if the cached plan still + // we will need to do the runtime planning to check if the cached plan still // performs efficiently, or requires re-planning. tassert(6693503, "PlanStageData must be present", planStageData); const bool hasHashLookup = !planStageData->foreignHashJoinCollections.empty(); @@ -1411,7 +1428,6 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getSlotBasedExe // Prepare the SBE tree for execution. stage_builder::prepareSlotBasedExecutableTree( opCtx, root.get(), &data, *cq, collections, yieldPolicy.get(), true); - return plan_executor_factory::make(opCtx, std::move(cq), std::move(solutions[0]), @@ -1420,7 +1436,8 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getSlotBasedExe collections, plannerParams.options, std::move(nss), - std::move(yieldPolicy)); + std::move(yieldPolicy), + planningResult->isRecoveredFromPlanCache()); } } // namespace diff --git a/src/mongo/db/query/plan_cache.h b/src/mongo/db/query/plan_cache.h index 752d5cfa4dd..2fc422c0ca2 100644 --- a/src/mongo/db/query/plan_cache.h +++ b/src/mongo/db/query/plan_cache.h @@ -706,8 +706,8 @@ private: // We do nothing. res.shouldBeCreated = false; } else if (newWorks > oldWorks) { - // This plan performed worse than expected. Rather than immediately overwriting the - // cache, lower the bar to what is considered good performance and keep the entry + // The cached plan performed worse than expected. Rather than immediately overwriting + // the cache, lower the bar to what is considered good performance and keep the entry // inactive. // Be sure that 'works' always grows by at least 1, in case its current @@ -725,7 +725,7 @@ private: res.shouldBeCreated = true; res.increasedWorks.emplace(increasedWorks); } else { - // This plan performed just as well or better than we expected, based on the + // This cached plan performed just as well or better than we expected, based on the // inactive entry's works. We use this as an indicator that it's safe to // cache (as an active entry) the plan this query used for the future. if (callbacks) { diff --git a/src/mongo/db/query/plan_executor_factory.cpp b/src/mongo/db/query/plan_executor_factory.cpp index 2403ff7c353..1ff8d5660b8 100644 --- a/src/mongo/db/query/plan_executor_factory.cpp +++ b/src/mongo/db/query/plan_executor_factory.cpp @@ -27,6 +27,7 @@ * it in the license file. */ +#include <iostream> #include "mongo/platform/basic.h" @@ -128,7 +129,8 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> make( const MultipleCollectionAccessor& collections, size_t plannerOptions, NamespaceString nss, - std::unique_ptr<PlanYieldPolicySBE> yieldPolicy) { + std::unique_ptr<PlanYieldPolicySBE> yieldPolicy, + bool planIsFromCache) { auto&& [rootStage, data] = root; LOGV2_DEBUG(4822860, @@ -137,17 +139,21 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> make( "slots"_attr = data.debugString(), "stages"_attr = sbe::DebugPrinter{}.print(*rootStage)); - return {{new PlanExecutorSBE( - opCtx, - std::move(cq), - std::move(optimizerData), - {makeVector<sbe::plan_ranker::CandidatePlan>(sbe::plan_ranker::CandidatePlan{ - std::move(solution), std::move(rootStage), std::move(data)}), - 0}, - plannerOptions & QueryPlannerParams::RETURN_OWNED_DATA, - std::move(nss), - false, - std::move(yieldPolicy)), + return {{new PlanExecutorSBE(opCtx, + std::move(cq), + std::move(optimizerData), + {makeVector<sbe::plan_ranker::CandidatePlan>( + sbe::plan_ranker::CandidatePlan{std::move(solution), + std::move(rootStage), + std::move(data), + false, + Status::OK(), + planIsFromCache}), + 0}, + plannerOptions & QueryPlannerParams::RETURN_OWNED_DATA, + std::move(nss), + false, + std::move(yieldPolicy)), PlanExecutor::Deleter{opCtx}}}; } diff --git a/src/mongo/db/query/plan_executor_factory.h b/src/mongo/db/query/plan_executor_factory.h index bf41f169af9..be7ab263a88 100644 --- a/src/mongo/db/query/plan_executor_factory.h +++ b/src/mongo/db/query/plan_executor_factory.h @@ -118,7 +118,8 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> make( const MultipleCollectionAccessor& collections, size_t plannerOptions, NamespaceString nss, - std::unique_ptr<PlanYieldPolicySBE> yieldPolicy); + std::unique_ptr<PlanYieldPolicySBE> yieldPolicy, + bool isFromPlanCache); /** * 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_sbe.cpp b/src/mongo/db/query/plan_executor_sbe.cpp index 920cd17715b..bd62138da46 100644 --- a/src/mongo/db/query/plan_executor_sbe.cpp +++ b/src/mongo/db/query/plan_executor_sbe.cpp @@ -27,7 +27,6 @@ * it in the license file. */ - #include "mongo/platform/basic.h" #include "mongo/db/query/plan_executor_sbe.h" @@ -102,9 +101,8 @@ PlanExecutorSBE::PlanExecutorSBE(OperationContext* opCtx, _yieldPolicy->clearRegisteredPlans(); _yieldPolicy->registerPlan(_root.get()); } - const auto isMultiPlan = candidates.plans.size() > 1; - + const auto isCachedCandidate = candidates.winner().isCachedCandidate; if (!_cq || !_cq->getExpCtx()->explain) { // If we're not in explain mode, there is no need to keep rejected candidate plans around. candidates.plans.clear(); @@ -123,6 +121,7 @@ PlanExecutorSBE::PlanExecutorSBE(OperationContext* opCtx, std::move(optimizerData), std::move(candidates.plans), isMultiPlan, + isCachedCandidate, _rootData.debugInfo); } diff --git a/src/mongo/db/query/plan_explainer_factory.cpp b/src/mongo/db/query/plan_explainer_factory.cpp index 544ab33fdd2..4f762aec167 100644 --- a/src/mongo/db/query/plan_explainer_factory.cpp +++ b/src/mongo/db/query/plan_explainer_factory.cpp @@ -57,6 +57,7 @@ std::unique_ptr<PlanExplainer> make(sbe::PlanStage* root, std::vector<sbe::plan_ranker::CandidatePlan> rejectedCandidates, bool isMultiPlan) { // Pre-compute Debugging info for explain use. + auto debugInfoSBE = std::make_shared<const plan_cache_debug_info::DebugInfoSBE>( plan_cache_util::buildDebugInfo(solution)); return std::make_unique<PlanExplainerSBE>(root, @@ -65,6 +66,7 @@ std::unique_ptr<PlanExplainer> make(sbe::PlanStage* root, std::move(optimizerData), std::move(rejectedCandidates), isMultiPlan, + false, /* isFromPlanCache */ debugInfoSBE); } @@ -75,6 +77,7 @@ std::unique_ptr<PlanExplainer> make( std::unique_ptr<optimizer::AbstractABTPrinter> optimizerData, std::vector<sbe::plan_ranker::CandidatePlan> rejectedCandidates, bool isMultiPlan, + bool isFromPlanCache, std::shared_ptr<const plan_cache_debug_info::DebugInfoSBE> debugInfoSBE) { // TODO SERVER-64882: Consider invariant(debugInfoSBE) as we may not need to create a // DebugInfoSBE from QuerySolution after the feature flag is removed. We currently need it @@ -90,6 +93,7 @@ std::unique_ptr<PlanExplainer> make( std::move(optimizerData), std::move(rejectedCandidates), isMultiPlan, + isFromPlanCache, debugInfoSBE); } } // namespace mongo::plan_explainer_factory diff --git a/src/mongo/db/query/plan_explainer_factory.h b/src/mongo/db/query/plan_explainer_factory.h index 5e24a755747..0b3c008304b 100644 --- a/src/mongo/db/query/plan_explainer_factory.h +++ b/src/mongo/db/query/plan_explainer_factory.h @@ -61,5 +61,6 @@ std::unique_ptr<PlanExplainer> make( std::unique_ptr<optimizer::AbstractABTPrinter> optimizerData, std::vector<sbe::plan_ranker::CandidatePlan> rejectedCandidates, bool isMultiPlan, + bool isFromPlanCache, std::shared_ptr<const plan_cache_debug_info::DebugInfoSBE> debugInfo); } // namespace mongo::plan_explainer_factory diff --git a/src/mongo/db/query/plan_explainer_impl.cpp b/src/mongo/db/query/plan_explainer_impl.cpp index 0b70636945a..dc118081b33 100644 --- a/src/mongo/db/query/plan_explainer_impl.cpp +++ b/src/mongo/db/query/plan_explainer_impl.cpp @@ -722,6 +722,10 @@ void PlanExplainerImpl::getSummaryStats(PlanSummaryStats* statsOut) const { const CachedPlanStats* cachedStats = static_cast<const CachedPlanStats*>(cachedPlan->getSpecificStats()); statsOut->replanReason = cachedStats->replanReason; + // Nonnull replanReason indicates cached plan was less effecient than expected and an + // alternative plan was chosen. + statsOut->replanReason ? statsOut->fromPlanCache = false + : statsOut->fromPlanCache = true; } else if (STAGE_MULTI_PLAN == stages[i]->stageType()) { statsOut->fromMultiPlanner = true; } else if (STAGE_COLLSCAN == stages[i]->stageType()) { diff --git a/src/mongo/db/query/plan_explainer_sbe.cpp b/src/mongo/db/query/plan_explainer_sbe.cpp index e14eebb59b4..fd4232ce1a9 100644 --- a/src/mongo/db/query/plan_explainer_sbe.cpp +++ b/src/mongo/db/query/plan_explainer_sbe.cpp @@ -370,6 +370,7 @@ void PlanExplainerSBE::getSummaryStats(PlanSummaryStats* statsOut) const { auto common = _root->getCommonStats(); statsOut->nReturned = common->advances; statsOut->fromMultiPlanner = isMultiPlan(); + statsOut->fromPlanCache = isFromCache(); statsOut->totalKeysExamined = 0; statsOut->totalDocsExamined = 0; statsOut->replanReason = _rootData->replanReason; diff --git a/src/mongo/db/query/plan_explainer_sbe.h b/src/mongo/db/query/plan_explainer_sbe.h index 5dc97f90641..f97e972de88 100644 --- a/src/mongo/db/query/plan_explainer_sbe.h +++ b/src/mongo/db/query/plan_explainer_sbe.h @@ -48,6 +48,7 @@ public: std::unique_ptr<optimizer::AbstractABTPrinter> optimizerData, std::vector<sbe::plan_ranker::CandidatePlan> rejectedCandidates, bool isMultiPlan, + bool isCachedPlan, std::shared_ptr<const plan_cache_debug_info::DebugInfoSBE> debugInfo) : PlanExplainer{solution}, _root{root}, @@ -56,6 +57,7 @@ public: _optimizerData(std::move(optimizerData)), _rejectedCandidates{std::move(rejectedCandidates)}, _isMultiPlan{isMultiPlan}, + _isFromPlanCache{isCachedPlan}, _debugInfo{debugInfo} { tassert(5968203, "_debugInfo should not be null", _debugInfo); } @@ -63,7 +65,9 @@ public: bool isMultiPlan() const final { return _isMultiPlan; } - + bool isFromCache() const { + return _isFromPlanCache; + } const ExplainVersion& getVersion() const final; std::string getPlanSummary() const final; void getSummaryStats(PlanSummaryStats* statsOut) const final; @@ -97,6 +101,7 @@ private: const std::vector<sbe::plan_ranker::CandidatePlan> _rejectedCandidates; const bool _isMultiPlan{false}; + const bool _isFromPlanCache{false}; // Pre-computed debugging info so we don't necessarily have to collect them from QuerySolution. // All plans recovered from the same cached entry share the same debug info. const std::shared_ptr<const plan_cache_debug_info::DebugInfoSBE> _debugInfo; diff --git a/src/mongo/db/query/plan_ranker.h b/src/mongo/db/query/plan_ranker.h index 8a6a2c18ad9..d456aa69d31 100644 --- a/src/mongo/db/query/plan_ranker.h +++ b/src/mongo/db/query/plan_ranker.h @@ -189,6 +189,8 @@ struct BaseCandidatePlan { // If the candidate plan has failed in a recoverable fashion during the trial run, contains a // non-OK status. Status status{Status::OK()}; + // Indicates whether this candidate plan was retrieved from the cache. + bool isCachedCandidate{false}; // Any results produced during the plan's execution prior to scoring are retained here. std::deque<ResultType> results; // This is used to track the original plan with clean PlanStage tree and the auxiliary data. diff --git a/src/mongo/db/query/plan_summary_stats.h b/src/mongo/db/query/plan_summary_stats.h index 59e3985662f..ae8e3e7abc8 100644 --- a/src/mongo/db/query/plan_summary_stats.h +++ b/src/mongo/db/query/plan_summary_stats.h @@ -110,6 +110,8 @@ struct PlanSummaryStats { // candidates? bool fromMultiPlanner = false; + // Was this plan recovered from the cache? + bool fromPlanCache = false; // Was a replan triggered during the execution of this query? boost::optional<std::string> replanReason; diff --git a/src/mongo/db/query/sbe_cached_solution_planner.cpp b/src/mongo/db/query/sbe_cached_solution_planner.cpp index 0cbf5623a5f..e7584b1f3d1 100644 --- a/src/mongo/db/query/sbe_cached_solution_planner.cpp +++ b/src/mongo/db/query/sbe_cached_solution_planner.cpp @@ -93,7 +93,6 @@ CandidatePlans CachedSolutionPlanner::plan( _opCtx, _collections, _cq, *solutions[0], _yieldPolicy); } } - // If the '_decisionReads' is not present then we do not run a trial period, keeping the current // plan. if (!_decisionReads) { @@ -110,7 +109,9 @@ CandidatePlans CachedSolutionPlanner::plan( std::move(roots[0].first), std::move(roots[0].second), false /* exitedEarly*/, - Status::OK()}), + Status::OK(), + true, + /*isFromPlanCache */}), 0}; } @@ -131,6 +132,7 @@ CandidatePlans CachedSolutionPlanner::plan( {}, /* optimizedData */ {}, /* rejectedCandidates */ false, /* isMultiPlan */ + true, /* isFromPlanCache */ candidate.data.debugInfo ? std::make_unique<plan_cache_debug_info::DebugInfoSBE>(*candidate.data.debugInfo) : nullptr); @@ -185,7 +187,9 @@ plan_ranker::CandidatePlan CachedSolutionPlanner::collectExecutionStatsForCached std::move(root), std::move(data), false /* exitedEarly*/, - Status::OK()}; + Status::OK(), + true, + /*is Cached plan*/}; ON_BLOCK_EXIT([rootPtr = candidate.root.get()] { rootPtr->detachFromTrialRunTracker(); }); |