diff options
| author | Catalin Sumanaru <catalin.sumanaru@mongodb.com> | 2024-03-26 22:55:17 +0000 |
|---|---|---|
| committer | MongoDB Bot <mongo-bot@mongodb.com> | 2024-03-26 23:06:18 +0000 |
| commit | 6cebe5000bfd36252203ebb17f117a790d533c72 (patch) | |
| tree | c843d8d6fb5f0f73578ef374d4067c7ee223d57e | |
| parent | 6c527068fbcea1913bed991ed134d163dd80c34c (diff) | |
SERVER-88070 Throw ErrorCode::QueryPlanKilled for stale index views during stale cache replans (#20133)r8.0.0-alpha2
GitOrigin-RevId: 47d38dc2b65727d4f05a03d957467d34217eac26
4 files changed, 29 insertions, 11 deletions
diff --git a/src/mongo/db/query/classic_runtime_planner_for_sbe/cached_planner.cpp b/src/mongo/db/query/classic_runtime_planner_for_sbe/cached_planner.cpp index f5e8bb4471e..9700c9cf664 100644 --- a/src/mongo/db/query/classic_runtime_planner_for_sbe/cached_planner.cpp +++ b/src/mongo/db/query/classic_runtime_planner_for_sbe/cached_planner.cpp @@ -30,6 +30,7 @@ #include "mongo/db/query/classic_runtime_planner_for_sbe/planner_interface.h" #include "mongo/db/exec/trial_period_utils.h" +#include "mongo/db/query/all_indices_required_checker.h" #include "mongo/db/query/bind_input_params.h" #include "mongo/db/query/plan_executor_factory.h" #include "mongo/db/query/planner_analysis.h" @@ -96,6 +97,7 @@ void recoverWhereExpression(CanonicalQuery* canonicalQuery, */ sbe::plan_ranker::CandidatePlan collectExecutionStatsForCachedPlan( const PlannerDataForSBE& plannerData, + const AllIndicesRequiredChecker& indexExistenceChecker, std::unique_ptr<sbe::PlanStage> root, stage_builder::PlanStageData data, size_t maxTrialPeriodNumReads) { @@ -145,7 +147,7 @@ sbe::plan_ranker::CandidatePlan collectExecutionStatsForCachedPlan( plannerData.collections, *plannerData.cq, plannerData.sbeYieldPolicy.get(), - AllIndicesRequiredChecker{plannerData.collections}} + indexExistenceChecker} .executeCachedCandidateTrial(&candidate, maxTrialResults); return candidate; @@ -154,8 +156,13 @@ sbe::plan_ranker::CandidatePlan collectExecutionStatsForCachedPlan( // TODO SERVER-87466 Trigger replanning by throwing an exception, instead of creating another // planner. std::unique_ptr<PlannerInterface> replan(PlannerDataForSBE plannerData, + const AllIndicesRequiredChecker& indexExistenceChecker, std::string replanReason, bool shouldCache) { + // The trial run might have allowed DDL commands to be executed during yields. Check if the + // provided planner parameters still match the current view of the index catalog. + indexExistenceChecker.check(plannerData.opCtx, plannerData.collections); + // The plan drawn from the cache is being discarded, and should no longer be // registered with the yield policy. plannerData.sbeYieldPolicy->clearRegisteredPlans(); @@ -190,6 +197,7 @@ std::unique_ptr<PlannerInterface> replan(PlannerDataForSBE plannerData, std::unique_ptr<PlannerInterface> makePlannerForCacheEntry( PlannerDataForSBE plannerData, std::unique_ptr<sbe::CachedPlanHolder> cachedPlanHolder) { + AllIndicesRequiredChecker indexExistenceChecker{plannerData.collections}; const auto& decisionReads = cachedPlanHolder->decisionWorks; auto sbePlan = std::move(cachedPlanHolder->cachedPlan->root); auto planStageData = std::move(cachedPlanHolder->cachedPlan->planStageData); @@ -214,6 +222,7 @@ std::unique_ptr<PlannerInterface> makePlannerForCacheEntry( if (!QueryPlannerAnalysis::isEligibleForHashJoin(collectionInfo->second)) { return replan(std::move(plannerData), + indexExistenceChecker, str::stream() << "Foreign collection " << foreignCollection.toStringForErrorMsg() << " is not eligible for hash join anymore", @@ -230,8 +239,11 @@ std::unique_ptr<PlannerInterface> makePlannerForCacheEntry( } const size_t maxReadsBeforeReplan = internalQueryCacheEvictionRatio * *decisionReads; - auto candidate = collectExecutionStatsForCachedPlan( - plannerData, std::move(sbePlan), std::move(planStageData), maxReadsBeforeReplan); + auto candidate = collectExecutionStatsForCachedPlan(plannerData, + indexExistenceChecker, + std::move(sbePlan), + std::move(planStageData), + maxReadsBeforeReplan); tassert(8523801, "'debugInfo' should be initialized", candidate.data.stageData.debugInfo); auto explainer = plan_explainer_factory::make(candidate.root.get(), @@ -254,7 +266,10 @@ std::unique_ptr<PlannerInterface> makePlannerForCacheEntry( "error"_attr = candidate.status.toString()); std::string replanReason = str::stream() << "cached plan returned: " << candidate.status; recoverWhereExpression(plannerData.cq, std::move(candidate)); - return replan(std::move(plannerData), std::move(replanReason), /* shouldCache */ false); + return replan(std::move(plannerData), + indexExistenceChecker, + std::move(replanReason), + /* shouldCache */ false); } if (candidate.exitedEarly) { @@ -284,7 +299,10 @@ std::unique_ptr<PlannerInterface> makePlannerForCacheEntry( << "cached plan was less efficient than expected: expected trial execution to take " << decisionReads << " reads but it took at least " << numReads << " reads"; recoverWhereExpression(plannerData.cq, std::move(candidate)); - return replan(std::move(plannerData), std::move(replanReason), /* shouldCache */ true); + return replan(std::move(plannerData), + indexExistenceChecker, + std::move(replanReason), + /* shouldCache */ true); } // If the trial run did not exit early, it means no replanning is necessary and can return this diff --git a/src/mongo/db/query/classic_runtime_planner_for_sbe/multi_planner.cpp b/src/mongo/db/query/classic_runtime_planner_for_sbe/multi_planner.cpp index 837a55f9f72..706ae897f3a 100644 --- a/src/mongo/db/query/classic_runtime_planner_for_sbe/multi_planner.cpp +++ b/src/mongo/db/query/classic_runtime_planner_for_sbe/multi_planner.cpp @@ -120,9 +120,7 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> MultiPlanner::makeExecutor( MultiPlanner::SbePlanAndData MultiPlanner::_buildSbePlanAndUpdatePlanCache( const QuerySolution* winningSolution, const plan_ranker::PlanRankingDecision& ranking) { - auto sbePlanAndData = stage_builder::buildSlotBasedExecutableTree( - opCtx(), collections(), *cq(), *winningSolution, sbeYieldPolicy()); - sbePlanAndData.second.replanReason = std::move(_replanReason); + auto sbePlanAndData = prepareSbePlanAndData(*winningSolution, std::move(_replanReason)); plan_cache_util::updateSbePlanCacheFromClassicCandidates(opCtx(), collections(), _cachingMode, diff --git a/src/mongo/db/query/classic_runtime_planner_for_sbe/sub_planner.cpp b/src/mongo/db/query/classic_runtime_planner_for_sbe/sub_planner.cpp index 1cd83ff29e1..dad8d35fd62 100644 --- a/src/mongo/db/query/classic_runtime_planner_for_sbe/sub_planner.cpp +++ b/src/mongo/db/query/classic_runtime_planner_for_sbe/sub_planner.cpp @@ -69,9 +69,7 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> SubPlanner::makeExecutor( *cq(), std::move(solution), plannerParams().secondaryCollectionsInfo); } - auto sbePlanAndData = stage_builder::buildSlotBasedExecutableTree( - opCtx(), collections(), *cq(), *solution, sbeYieldPolicy()); - + auto sbePlanAndData = prepareSbePlanAndData(*solution); plan_cache_util::updatePlanCache(opCtx(), collections(), *cq(), diff --git a/src/mongo/db/query/sbe_cached_solution_planner.cpp b/src/mongo/db/query/sbe_cached_solution_planner.cpp index 15e83d05a29..259fdcd6fdf 100644 --- a/src/mongo/db/query/sbe_cached_solution_planner.cpp +++ b/src/mongo/db/query/sbe_cached_solution_planner.cpp @@ -262,6 +262,10 @@ CandidatePlans CachedSolutionPlanner::replan(const QueryPlannerParams& plannerPa return std::make_pair(std::move(root), std::move(data)); }; + // The trial run might have allowed DDL commands to be executed during yields. Check if the + // provided planner parameters still match the current view of the index catalog. + _indexExistenceChecker.check(_opCtx, _collections); + // Use the query planning module to plan the whole query. auto statusWithMultiPlanSolns = QueryPlanner::plan(_cq, plannerParams); auto solutions = uassertStatusOK(std::move(statusWithMultiPlanSolns)); |
