diff options
author | jannaerin <golden.janna@gmail.com> | 2017-10-27 12:53:48 -0400 |
---|---|---|
committer | jannaerin <golden.janna@gmail.com> | 2017-11-13 18:21:23 -0500 |
commit | bace2a3017ebd46ca96cc81f48f039b80e00d92a (patch) | |
tree | 591bfb62702350ecff78bb6d48bf333a849fc9fe /jstests/core | |
parent | 956a2d030f04e5bbb2213ac6052f10d82cd3ec74 (diff) | |
download | mongo-bace2a3017ebd46ca96cc81f48f039b80e00d92a.tar.gz |
SERVER-31610 Add timestamp to plan cache entries
Diffstat (limited to 'jstests/core')
-rw-r--r-- | jstests/core/collation_plan_cache.js | 17 | ||||
-rw-r--r-- | jstests/core/plan_cache_list_plans.js | 170 | ||||
-rw-r--r-- | jstests/core/plan_cache_shell_helpers.js | 16 |
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'); |