summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/commands/index_filter_commands_test.cpp5
-rw-r--r--src/mongo/db/commands/plan_cache_commands.cpp10
-rw-r--r--src/mongo/db/commands/plan_cache_commands_test.cpp77
-rw-r--r--src/mongo/db/exec/multi_plan.cpp5
-rw-r--r--src/mongo/db/query/plan_cache.cpp9
-rw-r--r--src/mongo/db/query/plan_cache.h8
-rw-r--r--src/mongo/db/query/plan_cache_test.cpp6
-rw-r--r--src/mongo/shell/collection.js6
-rw-r--r--src/mongo/shell/query.js2
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;
};
/**