diff options
author | Maddie Zechar <mez2113@columbia.edu> | 2022-10-10 16:22:03 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-10-10 17:16:27 +0000 |
commit | dbd868972c766d7a272e7cb3fcd2f3c792b2dd83 (patch) | |
tree | 03784fb830fc1005ff41b5f846f0715794309ccc /src | |
parent | df498b17710f364a853c4dc1731cbdc6ec91e0f5 (diff) | |
download | mongo-dbd868972c766d7a272e7cb3fcd2f3c792b2dd83.tar.gz |
SERVER-64978 Add metrics to track if a plan was recovered from the plan cache
Diffstat (limited to 'src')
-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 |
16 files changed, 82 insertions, 28 deletions
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(); }); |