diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/commands/index_filter_commands_test.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/commands/plan_cache_commands.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/commands/plan_cache_commands_test.cpp | 77 | ||||
-rw-r--r-- | src/mongo/db/exec/multi_plan.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/query/plan_cache.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/query/plan_cache.h | 8 | ||||
-rw-r--r-- | src/mongo/db/query/plan_cache_test.cpp | 6 | ||||
-rw-r--r-- | src/mongo/shell/collection.js | 6 | ||||
-rw-r--r-- | src/mongo/shell/query.js | 2 |
9 files changed, 101 insertions, 27 deletions
diff --git a/src/mongo/db/commands/index_filter_commands_test.cpp b/src/mongo/db/commands/index_filter_commands_test.cpp index 17dec1cb003..da0b0b919d0 100644 --- a/src/mongo/db/commands/index_filter_commands_test.cpp +++ b/src/mongo/db/commands/index_filter_commands_test.cpp @@ -140,7 +140,10 @@ void addQueryShapeToPlanCache(OperationContext* opCtx, qs.cacheData->tree.reset(new PlanCacheIndexTree()); std::vector<QuerySolution*> solns; solns.push_back(&qs); - ASSERT_OK(planCache->add(*cq, solns, createDecision(1U))); + ASSERT_OK(planCache->add(*cq, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); } /** diff --git a/src/mongo/db/commands/plan_cache_commands.cpp b/src/mongo/db/commands/plan_cache_commands.cpp index 3704105d17f..002a4e9d94d 100644 --- a/src/mongo/db/commands/plan_cache_commands.cpp +++ b/src/mongo/db/commands/plan_cache_commands.cpp @@ -406,10 +406,8 @@ Status PlanCacheListPlans::list(OperationContext* opCtx, for (size_t i = 0; i < numPlans; ++i) { BSONObjBuilder planBob(plansBuilder.subobjStart()); - // Create plan details field. - // Currently, simple string representationg of - // SolutionCacheData. Need to revisit format when we - // need to parse user-provided plan details for planCacheAddPlan. + // Create the plan details field. Currently, this is a simple string representation of + // SolutionCacheData. SolutionCacheData* scd = entry->plannerData[i]; BSONObjBuilder detailsBob(planBob.subobjStart("details")); detailsBob.append("solution", scd->toString()); @@ -442,8 +440,12 @@ Status PlanCacheListPlans::list(OperationContext* opCtx, planBob.append("filterSet", scd->indexFilterApplied); } + plansBuilder.doneFast(); + // Append the time the entry was inserted into the plan cache. + bob->append("timeOfCreation", entry->timeOfCreation); + return Status::OK(); } diff --git a/src/mongo/db/commands/plan_cache_commands_test.cpp b/src/mongo/db/commands/plan_cache_commands_test.cpp index b93b9da43e2..fb0ed6c3e03 100644 --- a/src/mongo/db/commands/plan_cache_commands_test.cpp +++ b/src/mongo/db/commands/plan_cache_commands_test.cpp @@ -151,7 +151,10 @@ TEST(PlanCacheCommandsTest, planCacheListQueryShapesOneKey) { qs.cacheData.reset(createSolutionCacheData()); std::vector<QuerySolution*> solns; solns.push_back(&qs); - planCache.add(*cq, solns, createDecision(1U)).transitional_ignore(); + ASSERT_OK(planCache.add(*cq, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); vector<BSONObj> shapes = getShapes(planCache); ASSERT_EQUALS(shapes.size(), 1U); @@ -183,7 +186,10 @@ TEST(PlanCacheCommandsTest, planCacheClearAllShapes) { qs.cacheData.reset(createSolutionCacheData()); std::vector<QuerySolution*> solns; solns.push_back(&qs); - planCache.add(*cq, solns, createDecision(1U)).transitional_ignore(); + ASSERT_OK(planCache.add(*cq, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); ASSERT_EQUALS(getShapes(planCache).size(), 1U); // Clear cache and confirm number of keys afterwards. @@ -339,8 +345,14 @@ TEST(PlanCacheCommandsTest, planCacheClearOneKey) { qs.cacheData.reset(createSolutionCacheData()); std::vector<QuerySolution*> solns; solns.push_back(&qs); - planCache.add(*cqA, solns, createDecision(1U)).transitional_ignore(); - planCache.add(*cqB, solns, createDecision(1U)).transitional_ignore(); + ASSERT_OK(planCache.add(*cqA, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); + ASSERT_OK(planCache.add(*cqB, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); // Check keys in cache before dropping {b: 1} vector<BSONObj> shapesBefore = getShapes(planCache); @@ -396,8 +408,14 @@ TEST(PlanCacheCommandsTest, planCacheClearOneKeyCollation) { qs.cacheData.reset(createSolutionCacheData()); std::vector<QuerySolution*> solns; solns.push_back(&qs); - planCache.add(*cq, solns, createDecision(1U)).transitional_ignore(); - planCache.add(*cqCollation, solns, createDecision(1U)).transitional_ignore(); + ASSERT_OK(planCache.add(*cq, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); + ASSERT_OK(planCache.add(*cqCollation, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); // Check keys in cache before dropping the query with collation. vector<BSONObj> shapesBefore = getShapes(planCache); @@ -539,7 +557,10 @@ TEST(PlanCacheCommandsTest, planCacheListPlansOnlyOneSolutionTrue) { qs.cacheData.reset(createSolutionCacheData()); std::vector<QuerySolution*> solns; solns.push_back(&qs); - planCache.add(*cq, solns, createDecision(1U)).transitional_ignore(); + ASSERT_OK(planCache.add(*cq, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); vector<BSONObj> plans = getPlans(planCache, cq->getQueryObj(), @@ -568,7 +589,10 @@ TEST(PlanCacheCommandsTest, planCacheListPlansOnlyOneSolutionFalse) { std::vector<QuerySolution*> solns; solns.push_back(&qs); solns.push_back(&qs); - planCache.add(*cq, solns, createDecision(2U)).transitional_ignore(); + ASSERT_OK(planCache.add(*cq, + solns, + createDecision(2U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); vector<BSONObj> plans = getPlans(planCache, cq->getQueryObj(), @@ -605,11 +629,17 @@ TEST(PlanCacheCommandsTest, planCacheListPlansCollation) { qs.cacheData.reset(createSolutionCacheData()); std::vector<QuerySolution*> solns; solns.push_back(&qs); - planCache.add(*cq, solns, createDecision(1U)).transitional_ignore(); + ASSERT_OK(planCache.add(*cq, + solns, + createDecision(1U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); std::vector<QuerySolution*> twoSolns; twoSolns.push_back(&qs); twoSolns.push_back(&qs); - planCache.add(*cqCollation, twoSolns, createDecision(2U)).transitional_ignore(); + ASSERT_OK(planCache.add(*cqCollation, + twoSolns, + createDecision(2U), + opCtx->getServiceContext()->getPreciseClockSource()->now())); // Normal query should have one solution. vector<BSONObj> plans = getPlans(planCache, @@ -628,4 +658,31 @@ TEST(PlanCacheCommandsTest, planCacheListPlansCollation) { ASSERT_EQUALS(plansCollation.size(), 2U); } +TEST(PlanCacheCommandsTest, planCacheListPlansTimeOfCreationIsCorrect) { + QueryTestServiceContext serviceContext; + auto opCtx = serviceContext.makeOperationContext(); + + // Create a canonical query. + auto qr = stdx::make_unique<QueryRequest>(nss); + qr->setFilter(fromjson("{a: 1}")); + auto statusWithCQ = CanonicalQuery::canonicalize(opCtx.get(), std::move(qr)); + ASSERT_OK(statusWithCQ.getStatus()); + auto cq = std::move(statusWithCQ.getValue()); + + // Plan cache with one entry. + PlanCache planCache; + QuerySolution qs; + qs.cacheData.reset(createSolutionCacheData()); + std::vector<QuerySolution*> solns; + solns.push_back(&qs); + auto now = opCtx->getServiceContext()->getPreciseClockSource()->now(); + ASSERT_OK(planCache.add(*cq, solns, createDecision(1U), now)); + + PlanCacheEntry* out; + ASSERT_OK(planCache.getEntry(*cq, &out)); + unique_ptr<PlanCacheEntry> entry(out); + + ASSERT_EQ(entry->timeOfCreation, now); +} + } // namespace diff --git a/src/mongo/db/exec/multi_plan.cpp b/src/mongo/db/exec/multi_plan.cpp index 4b32b06ade4..d4c9e732717 100644 --- a/src/mongo/db/exec/multi_plan.cpp +++ b/src/mongo/db/exec/multi_plan.cpp @@ -324,7 +324,10 @@ Status MultiPlanStage::pickBestPlan(PlanYieldPolicy* yieldPolicy) { if (validSolutions) { _collection->infoCache() ->getPlanCache() - ->add(*_query, solutions, ranking.release()) + ->add(*_query, + solutions, + ranking.release(), + getOpCtx()->getServiceContext()->getPreciseClockSource()->now()) .transitional_ignore(); } } diff --git a/src/mongo/db/query/plan_cache.cpp b/src/mongo/db/query/plan_cache.cpp index 43502d96c0b..577cacd3a2b 100644 --- a/src/mongo/db/query/plan_cache.cpp +++ b/src/mongo/db/query/plan_cache.cpp @@ -433,6 +433,7 @@ PlanCacheEntry* PlanCacheEntry::clone() const { entry->sort = sort.getOwned(); entry->projection = projection.getOwned(); entry->collation = collation.getOwned(); + entry->timeOfCreation = timeOfCreation; // Copy performance stats. for (size_t i = 0; i < feedback.size(); ++i) { @@ -448,7 +449,8 @@ std::string PlanCacheEntry::toString() const { return str::stream() << "(query: " << query.toString() << ";sort: " << sort.toString() << ";projection: " << projection.toString() << ";collation: " << collation.toString() - << ";solutions: " << plannerData.size() << ")"; + << ";solutions: " << plannerData.size() + << ";timeOfCreation: " << timeOfCreation.toString() << ")"; } std::string CachedSolution::toString() const { @@ -694,7 +696,8 @@ void PlanCache::encodeKeyForProj(const BSONObj& projObj, StringBuilder* keyBuild Status PlanCache::add(const CanonicalQuery& query, const std::vector<QuerySolution*>& solns, - PlanRankingDecision* why) { + PlanRankingDecision* why, + Date_t now) { invariant(why); if (solns.empty()) { @@ -721,6 +724,8 @@ Status PlanCache::add(const CanonicalQuery& query, if (query.getCollator()) { entry->collation = query.getCollator()->getSpec().toBSON(); } + entry->timeOfCreation = now; + // Strip projections on $-prefixed fields, as these are added by internal callers of the query // system and are not considered part of the user projection. diff --git a/src/mongo/db/query/plan_cache.h b/src/mongo/db/query/plan_cache.h index c532a514640..e97e57db06b 100644 --- a/src/mongo/db/query/plan_cache.h +++ b/src/mongo/db/query/plan_cache.h @@ -267,6 +267,7 @@ public: BSONObj sort; BSONObj projection; BSONObj collation; + Date_t timeOfCreation; // // Performance stats @@ -312,7 +313,9 @@ public: * Record solutions for query. Best plan is first element in list. * Each query in the cache will have more than 1 plan because we only * add queries which are considered by the multi plan runner (which happens - * only when the query planner generates multiple candidate plans). + * only when the query planner generates multiple candidate plans). Callers are responsible + * for passing the current time so that the time the plan cache entry was created is stored + * in the plan cache. * * Takes ownership of 'why'. * @@ -321,7 +324,8 @@ public: */ Status add(const CanonicalQuery& query, const std::vector<QuerySolution*>& solns, - PlanRankingDecision* why); + PlanRankingDecision* why, + Date_t now); /** * Look up the cached data access for the provided 'query'. Used by the query planner diff --git a/src/mongo/db/query/plan_cache_test.cpp b/src/mongo/db/query/plan_cache_test.cpp index eb588910785..19115ccc1cb 100644 --- a/src/mongo/db/query/plan_cache_test.cpp +++ b/src/mongo/db/query/plan_cache_test.cpp @@ -427,7 +427,8 @@ TEST(PlanCacheTest, AddEmptySolutions) { unique_ptr<CanonicalQuery> cq(canonicalize("{a: 1}")); std::vector<QuerySolution*> solns; unique_ptr<PlanRankingDecision> decision(createDecision(1U)); - ASSERT_NOT_OK(planCache.add(*cq, solns, decision.get())); + QueryTestServiceContext serviceContext; + ASSERT_NOT_OK(planCache.add(*cq, solns, decision.get(), Date_t{})); } TEST(PlanCacheTest, AddValidSolution) { @@ -441,7 +442,8 @@ TEST(PlanCacheTest, AddValidSolution) { // Check if key is in cache before and after add(). ASSERT_FALSE(planCache.contains(*cq)); - ASSERT_OK(planCache.add(*cq, solns, createDecision(1U))); + QueryTestServiceContext serviceContext; + ASSERT_OK(planCache.add(*cq, solns, createDecision(1U), Date_t{})); ASSERT_TRUE(planCache.contains(*cq)); ASSERT_EQUALS(planCache.size(), 1U); diff --git a/src/mongo/shell/collection.js b/src/mongo/shell/collection.js index 998d4db3e90..8d242a347a9 100644 --- a/src/mongo/shell/collection.js +++ b/src/mongo/shell/collection.js @@ -1780,10 +1780,8 @@ PlanCache.prototype.clear = function() { * List plans for a query shape. */ PlanCache.prototype.getPlansByQuery = function(query, projection, sort, collation) { - return this - ._runCommandThrowOnError("planCacheListPlans", - this._parseQueryShape(query, projection, sort, collation)) - .plans; + return this._runCommandThrowOnError("planCacheListPlans", + this._parseQueryShape(query, projection, sort, collation)); }; /** diff --git a/src/mongo/shell/query.js b/src/mongo/shell/query.js index 7b1cf7be1b5..6de7edb3661 100644 --- a/src/mongo/shell/query.js +++ b/src/mongo/shell/query.js @@ -905,7 +905,7 @@ QueryPlan.prototype.help = function() { * List plans for a query shape. */ QueryPlan.prototype.getPlans = function() { - return this._cursor._collection.getPlanCache().getPlansByQuery(this._cursor); + return this._cursor._collection.getPlanCache().getPlansByQuery(this._cursor).plans; }; /** |