diff options
author | Ruoxin Xu <ruoxin.xu@mongodb.com> | 2021-09-24 13:43:15 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-09-24 14:23:54 +0000 |
commit | 634867703b7eb40f073198d27633a7c5506e4604 (patch) | |
tree | 70a161bc3cb70f3f3fca99dc64f4e179320df8e2 /src/mongo/db/query/query_planner.h | |
parent | c607f9b63f4764335749ae40a5762d6a305558c1 (diff) | |
download | mongo-634867703b7eb40f073198d27633a7c5506e4604.tar.gz |
SERVER-59854 Remove PlanCacheIndexabilityState from the PlanCache
This patch includes a number of changes to facilitate the use of PlanCacheIndexabilityState with SBE and classic plan cache keys.
* Introduced a plan cache key factory.
* Moved PlanCacheIndexabilityState from the PlanCache into CollectionQueryInfo.
* Templetized QueryPlanner::planSubqueries() so it can be used with classic and SBE plan caches.
* Removed dependency on the CanonicalQuery from the PlanCache class.
Co-authored-by: Anton Korshunov <anton.korshunov@mongodb.com>
Diffstat (limited to 'src/mongo/db/query/query_planner.h')
-rw-r--r-- | src/mongo/db/query/query_planner.h | 105 |
1 files changed, 99 insertions, 6 deletions
diff --git a/src/mongo/db/query/query_planner.h b/src/mongo/db/query/query_planner.h index 6d4fc4d1500..817afff0dea 100644 --- a/src/mongo/db/query/query_planner.h +++ b/src/mongo/db/query/query_planner.h @@ -36,6 +36,16 @@ #include "mongo/db/query/query_solution.h" namespace mongo { +// The logging facility enforces the rule that logging should not be done in a header file. Since +// template classes and functions below must be defined in the header file and since they use the +// logging facility, we have to define the helper functions below to perform the actual logging +// operation from template code. +namespace log_detail { +void logSubplannerIndexEntry(const IndexEntry& entry, size_t childIndex); +void logCachedPlanFound(size_t numChildren, size_t childIndex); +void logCachedPlanNotFound(size_t numChildren, size_t childIndex); +void logNumberOfSolutions(size_t numSolutions); +} // namespace log_detail class Collection; class CollectionPtr; @@ -119,14 +129,21 @@ public: const CachedSolution& cachedSoln); /** - * Plan each branch of the rooted $or query independently, and store the resulting + * Plan each branch of the rooted $or query independently, and return the resulting * lists of query solutions in 'SubqueriesPlanningResult'. + * + * The 'createPlanCacheKey' callback is used to create a plan cache key of the specified + * 'KeyType' for each of the branches to look up the plan in the 'planCache'. */ - static StatusWith<SubqueriesPlanningResult> planSubqueries(OperationContext* opCtx, - const CollectionPtr& collection, - const PlanCache* planCache, - const CanonicalQuery& query, - const QueryPlannerParams& params); + template <typename KeyType, typename... Args> + static StatusWith<SubqueriesPlanningResult> planSubqueries( + OperationContext* opCtx, + const PlanCacheBase<KeyType, Args...>* planCache, + std::function<KeyType(const CanonicalQuery& cq, const CollectionPtr& coll)> + createPlanCacheKey, + const CollectionPtr& collection, + const CanonicalQuery& query, + const QueryPlannerParams& params); /** * Generates and returns the index tag tree that will be inserted into the plan cache. This data @@ -174,4 +191,80 @@ public: std::function<StatusWith<std::unique_ptr<QuerySolution>>( CanonicalQuery* cq, std::vector<std::unique_ptr<QuerySolution>>)> multiplanCallback); }; + +template <typename KeyType, typename... Args> +StatusWith<QueryPlanner::SubqueriesPlanningResult> QueryPlanner::planSubqueries( + OperationContext* opCtx, + const PlanCacheBase<KeyType, Args...>* planCache, + std::function<KeyType(const CanonicalQuery& cq, const CollectionPtr& coll)> createPlanCacheKey, + const CollectionPtr& collection, + const CanonicalQuery& query, + const QueryPlannerParams& params) { + invariant(query.root()->matchType() == MatchExpression::OR); + invariant(query.root()->numChildren(), "Cannot plan subqueries for an $or with no children"); + + SubqueriesPlanningResult planningResult{query.root()->shallowClone()}; + for (size_t i = 0; i < params.indices.size(); ++i) { + const IndexEntry& ie = params.indices[i]; + const auto insertionRes = planningResult.indexMap.insert(std::make_pair(ie.identifier, i)); + // Be sure the key was not already in the map. + invariant(insertionRes.second); + log_detail::logSubplannerIndexEntry(ie, i); + } + + for (size_t i = 0; i < planningResult.orExpression->numChildren(); ++i) { + // We need a place to shove the results from planning this branch. + planningResult.branches.push_back( + std::make_unique<SubqueriesPlanningResult::BranchPlanningResult>()); + auto branchResult = planningResult.branches.back().get(); + auto orChild = planningResult.orExpression->getChild(i); + + // Turn the i-th child into its own query. + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, query, orChild); + if (!statusWithCQ.isOK()) { + str::stream ss; + ss << "Can't canonicalize subchild " << orChild->debugString() << " " + << statusWithCQ.getStatus().reason(); + return Status(ErrorCodes::BadValue, ss); + } + + branchResult->canonicalQuery = std::move(statusWithCQ.getValue()); + + // Plan the i-th child. We might be able to find a plan for the i-th child in the plan + // cache. If there's no cached plan, then we generate and rank plans using the MPS. + + // Populate branchResult->cachedSolution if an active cachedSolution entry exists. + if (planCache && shouldCacheQuery(*branchResult->canonicalQuery)) { + if (auto cachedSol = planCache->getCacheEntryIfActive( + createPlanCacheKey(*branchResult->canonicalQuery, collection))) { + // We have a CachedSolution. Store it for later. + log_detail::logCachedPlanFound(planningResult.orExpression->numChildren(), i); + + branchResult->cachedSolution = std::move(cachedSol); + } + } + + if (!branchResult->cachedSolution) { + // No CachedSolution found. We'll have to plan from scratch. + log_detail::logCachedPlanNotFound(planningResult.orExpression->numChildren(), i); + + // We don't set NO_TABLE_SCAN because peeking at the cache data will keep us from + // considering any plan that's a collscan. + invariant(branchResult->solutions.empty()); + auto statusWithMultiPlanSolns = + QueryPlanner::planForMultiPlanner(*branchResult->canonicalQuery, params); + if (!statusWithMultiPlanSolns.isOK()) { + str::stream ss; + ss << "Can't plan for subchild " << branchResult->canonicalQuery->toString() << " " + << statusWithMultiPlanSolns.getStatus().reason(); + return Status(ErrorCodes::BadValue, ss); + } + branchResult->solutions = std::move(statusWithMultiPlanSolns.getValue()); + + log_detail::logNumberOfSolutions(branchResult->solutions.size()); + } + } + + return std::move(planningResult); +} } // namespace mongo |