summaryrefslogtreecommitdiff
path: root/jstests/core
diff options
context:
space:
mode:
authorjannaerin <golden.janna@gmail.com>2017-10-27 12:53:48 -0400
committerjannaerin <golden.janna@gmail.com>2017-11-13 18:21:23 -0500
commitbace2a3017ebd46ca96cc81f48f039b80e00d92a (patch)
tree591bfb62702350ecff78bb6d48bf333a849fc9fe /jstests/core
parent956a2d030f04e5bbb2213ac6052f10d82cd3ec74 (diff)
downloadmongo-bace2a3017ebd46ca96cc81f48f039b80e00d92a.tar.gz
SERVER-31610 Add timestamp to plan cache entries
Diffstat (limited to 'jstests/core')
-rw-r--r--jstests/core/collation_plan_cache.js17
-rw-r--r--jstests/core/plan_cache_list_plans.js170
-rw-r--r--jstests/core/plan_cache_shell_helpers.js16
3 files changed, 115 insertions, 88 deletions
diff --git a/jstests/core/collation_plan_cache.js b/jstests/core/collation_plan_cache.js
index 0eec77388e4..6cb938dbc36 100644
--- a/jstests/core/collation_plan_cache.js
+++ b/jstests/core/collation_plan_cache.js
@@ -72,20 +72,21 @@
coll.getPlanCache()
.getPlansByQuery(
{query: {a: 'foo', b: 5}, sort: {}, projection: {}, collation: {locale: 'en_US'}})
- .length,
+ .plans.length,
'unexpected number of cached plans for query');
// Test passing the query, sort, projection, and collation to getPlansByQuery() as separate
// arguments.
- assert.lt(
- 0,
- coll.getPlanCache().getPlansByQuery({a: 'foo', b: 5}, {}, {}, {locale: 'en_US'}).length,
- 'unexpected number of cached plans for query');
+ assert.lt(0,
+ coll.getPlanCache()
+ .getPlansByQuery({a: 'foo', b: 5}, {}, {}, {locale: 'en_US'})
+ .plans.length,
+ 'unexpected number of cached plans for query');
// Test passing the query, sort, projection, and collation to getPlansByQuery() as separate
// arguments.
assert.eq(0,
- coll.getPlanCache().getPlansByQuery({a: 'foo', b: 5}).length,
+ coll.getPlanCache().getPlansByQuery({a: 'foo', b: 5}).plans.length,
'unexpected number of cached plans for query');
// A query with a different collation should have no cached plans.
@@ -94,7 +95,7 @@
coll.getPlanCache()
.getPlansByQuery(
{query: {a: 'foo', b: 5}, sort: {}, projection: {}, collation: {locale: 'fr_CA'}})
- .length,
+ .plans.length,
'unexpected number of cached plans for query');
// A query with different string locations should have no cached plans.
@@ -106,7 +107,7 @@
projection: {},
collation: {locale: 'en_US'}
})
- .length,
+ .plans.length,
'unexpected number of cached plans for query');
coll.getPlanCache().clear();
diff --git a/jstests/core/plan_cache_list_plans.js b/jstests/core/plan_cache_list_plans.js
index 7ca599483ff..4da32686ae6 100644
--- a/jstests/core/plan_cache_list_plans.js
+++ b/jstests/core/plan_cache_list_plans.js
@@ -1,75 +1,101 @@
// Test the planCacheListPlans command.
-var t = db.jstests_plan_cache_list_plans;
-t.drop();
-
-// Utility function to list plans for a query.
-function getPlans(query, sort, projection) {
- var key = {query: query, sort: sort, projection: projection};
- var res = t.runCommand('planCacheListPlans', key);
- assert.commandWorked(res, 'planCacheListPlans(' + tojson(key, '', true) + ' failed');
- assert(res.hasOwnProperty('plans'),
- 'plans missing from planCacheListPlans(' + tojson(key, '', true) + ') result');
- return res.plans;
-}
-
-t.save({a: 1, b: 1});
-t.save({a: 1, b: 2});
-t.save({a: 1, b: 2});
-t.save({a: 2, b: 2});
-
-// We need two indices so that the MultiPlanRunner is executed.
-t.ensureIndex({a: 1});
-t.ensureIndex({a: 1, b: 1});
-
-// Invalid key should be an error.
-assert.eq(0,
- getPlans({unknownfield: 1}, {}, {}),
- 'planCacheListPlans should return empty results on unknown query shape');
-
-// Create a cache entry.
-assert.eq(
- 1, t.find({a: 1, b: 1}, {_id: 0, a: 1}).sort({a: -1}).itcount(), 'unexpected document count');
-
-// Retrieve plans for valid cache entry.
-var plans = getPlans({a: 1, b: 1}, {a: -1}, {_id: 0, a: 1});
-assert.eq(2, plans.length, 'unexpected number of plans cached for query');
-
-// Print every plan
-// Plan details/feedback verified separately in section after Query Plan Revision tests.
-print('planCacheListPlans result:');
-for (var i = 0; i < plans.length; i++) {
- print('plan ' + i + ': ' + tojson(plans[i]));
-}
-
-//
-// Tests for plan reason and feedback in planCacheListPlans
-//
-
-// Generate more plans for test query by adding indexes (compound and sparse).
-// This will also clear the plan cache.
-t.ensureIndex({a: -1}, {sparse: true});
-t.ensureIndex({a: 1, b: 1});
-
-// Implementation note: feedback stats is calculated after 20 executions.
-// See PlanCacheEntry::kMaxFeedback.
-var numExecutions = 100;
-for (var i = 0; i < numExecutions; i++) {
- assert.eq(0, t.find({a: 3, b: 3}, {_id: 0, a: 1}).sort({a: -1}).itcount(), 'query failed');
-}
-
-plans = getPlans({a: 3, b: 3}, {a: -1}, {_id: 0, a: 1});
-
-// This should be obvious but feedback is available only for the first (winning) plan.
-print('planCacheListPlans result (after adding indexes and completing 20 executions):');
-for (var i = 0; i < plans.length; i++) {
- print('plan ' + i + ': ' + tojson(plans[i]));
- assert.gt(plans[i].reason.score, 0, 'plan ' + i + ' score is invalid');
- if (i > 0) {
- assert.lte(plans[i].reason.score,
- plans[i - 1].reason.score,
- 'plans not sorted by score in descending order. ' +
- 'plan ' + i + ' has a score that is greater than that of the previous plan');
+(function() {
+ "use strict";
+ let t = db.jstests_plan_cache_list_plans;
+ t.drop();
+
+ // Utility function to list plans for a query.
+ function getPlans(query, sort, projection) {
+ let key = {query: query, sort: sort, projection: projection};
+ let res = t.runCommand('planCacheListPlans', key);
+ assert.commandWorked(res, 'planCacheListPlans(' + tojson(key, '', true) + ' failed');
+ assert(res.hasOwnProperty('plans'),
+ 'plans missing from planCacheListPlans(' + tojson(key, '', true) + ') result');
+ return res.plans;
+ }
+
+ // Assert that timeOfCreation exists in the cache entry. The difference between the current time
+ // and
+ // the time a plan was cached should not be larger than an hour.
+ function checkTimeOfCreation(query, sort, projection, date) {
+ let key = {query: query, sort: sort, projection: projection};
+ let res = t.runCommand('planCacheListPlans', key);
+ assert.commandWorked(res, 'planCacheListPlans(' + tojson(key, '', true) + ' failed');
+ assert(res.hasOwnProperty('timeOfCreation'),
+ 'timeOfCreation missing from planCacheListPlans');
+ let kMillisecondsPerHour = 1000 * 60 * 60;
+ assert.lte(Math.abs(date - res.timeOfCreation.getTime()),
+ kMillisecondsPerHour,
+ 'timeOfCreation value is incorrect');
+ }
+
+ t.save({a: 1, b: 1});
+ t.save({a: 1, b: 2});
+ t.save({a: 1, b: 2});
+ t.save({a: 2, b: 2});
+
+ // We need two indices so that the MultiPlanRunner is executed.
+ t.ensureIndex({a: 1});
+ t.ensureIndex({a: 1, b: 1});
+
+ // Invalid key should be an error.
+ assert.eq(0,
+ getPlans({unknownfield: 1}, {}, {}),
+ 'planCacheListPlans should return empty results on unknown query shape');
+
+ // Create a cache entry.
+ assert.eq(1,
+ t.find({a: 1, b: 1}, {_id: 0, a: 1}).sort({a: -1}).itcount(),
+ 'unexpected document count');
+
+ let now = (new Date()).getTime();
+ checkTimeOfCreation({a: 1, b: 1}, {a: -1}, {_id: 0, a: 1}, now);
+
+ // Retrieve plans for valid cache entry.
+ let plans = getPlans({a: 1, b: 1}, {a: -1}, {_id: 0, a: 1});
+ assert.eq(2, plans.length, 'unexpected number of plans cached for query');
+
+ // Print every plan
+ // Plan details/feedback verified separately in section after Query Plan Revision tests.
+ print('planCacheListPlans result:');
+ for (let i = 0; i < plans.length; i++) {
+ print('plan ' + i + ': ' + tojson(plans[i]));
+ }
+
+ //
+ // Tests for plan reason and feedback in planCacheListPlans
+ //
+
+ // Generate more plans for test query by adding indexes (compound and sparse).
+ // This will also clear the plan cache.
+ t.ensureIndex({a: -1}, {sparse: true});
+ t.ensureIndex({a: 1, b: 1});
+
+ // Implementation note: feedback stats is calculated after 20 executions.
+ // See PlanCacheEntry::kMaxFeedback.
+ let numExecutions = 100;
+ for (let i = 0; i < numExecutions; i++) {
+ assert.eq(0, t.find({a: 3, b: 3}, {_id: 0, a: 1}).sort({a: -1}).itcount(), 'query failed');
+ }
+
+ now = (new Date()).getTime();
+ checkTimeOfCreation({a: 3, b: 3}, {a: -1}, {_id: 0, a: 1}, now);
+
+ plans = getPlans({a: 3, b: 3}, {a: -1}, {_id: 0, a: 1});
+
+ // This should be obvious but feedback is available only for the first (winning) plan.
+ print('planCacheListPlans result (after adding indexes and completing 20 executions):');
+ for (let i = 0; i < plans.length; i++) {
+ print('plan ' + i + ': ' + tojson(plans[i]));
+ assert.gt(plans[i].reason.score, 0, 'plan ' + i + ' score is invalid');
+ if (i > 0) {
+ assert.lte(plans[i].reason.score,
+ plans[i - 1].reason.score,
+ 'plans not sorted by score in descending order. ' +
+ 'plan ' + i +
+ ' has a score that is greater than that of the previous plan');
+ }
+ assert(plans[i].reason.stats.hasOwnProperty('stage'), 'no stats inserted for plan ' + i);
}
- assert(plans[i].reason.stats.hasOwnProperty('stage'), 'no stats inserted for plan ' + i);
-}
+})();
diff --git a/jstests/core/plan_cache_shell_helpers.js b/jstests/core/plan_cache_shell_helpers.js
index dc990b19dcc..ec4ab6df693 100644
--- a/jstests/core/plan_cache_shell_helpers.js
+++ b/jstests/core/plan_cache_shell_helpers.js
@@ -82,7 +82,7 @@ assert.eq(getShapes(),
// should return empty array on non-existent query shape.
assert.eq(0,
- planCache.getPlansByQuery({unknownfield: 1}).length,
+ planCache.getPlansByQuery({unknownfield: 1}).plans.length,
'collection.getPlanCache().getPlansByQuery() should return empty results ' +
'on non-existent collection');
// should error on missing required field query.
@@ -92,16 +92,16 @@ assert.throws(function() {
// Invoke with various permutations of required (query) and optional (projection, sort) arguments.
assert.eq(getPlans(queryB, sortC, projectionB),
- planCache.getPlansByQuery(queryB, projectionB, sortC),
+ planCache.getPlansByQuery(queryB, projectionB, sortC).plans,
'plans from collection.getPlanCache().getPlansByQuery() different from command result');
assert.eq(getPlans(queryB, {}, projectionB),
- planCache.getPlansByQuery(queryB, projectionB),
+ planCache.getPlansByQuery(queryB, projectionB).plans,
'plans from collection.getPlanCache().getPlansByQuery() different from command result');
assert.eq(getPlans(queryB, sortC, {}),
- planCache.getPlansByQuery(queryB, undefined, sortC),
+ planCache.getPlansByQuery(queryB, undefined, sortC).plans,
'plans from collection.getPlanCache().getPlansByQuery() different from command result');
assert.eq(getPlans(queryB, {}, {}),
- planCache.getPlansByQuery(queryB),
+ planCache.getPlansByQuery(queryB).plans,
'plans from collection.getPlanCache().getPlansByQuery() different from command result');
// getPlansByQuery() will also accept a single argument with the query shape object
@@ -114,7 +114,7 @@ assert.eq(getPlans(queryB, {}, {}),
// }
var shapeB = {query: queryB, projection: projectionB, sort: sortC};
assert.eq(getPlans(queryB, sortC, projectionB),
- planCache.getPlansByQuery(shapeB),
+ planCache.getPlansByQuery(shapeB).plans,
'collection.getPlanCache().getPlansByQuery() did not accept query shape object');
// Should return empty array on missing or extra fields in query shape object.
@@ -122,14 +122,14 @@ assert.eq(getPlans(queryB, sortC, projectionB),
// as the 'query' component which will result in the server returning an empty
// array of plans.
assert.eq(0,
- planCache.getPlansByQuery({query: queryB}).length,
+ planCache.getPlansByQuery({query: queryB}).plans.length,
'collection.getPlanCache.getPlansByQuery should return empty results on ' +
'incomplete query shape');
assert.eq(
0,
planCache
.getPlansByQuery({query: queryB, sort: sortC, projection: projectionB, unknown_field: 1})
- .length,
+ .plans.length,
'collection.getPlanCache.getPlansByQuery should return empty results on ' +
'invalid query shape');