diff options
Diffstat (limited to 'jstests/core/plan_cache_commands.js')
-rw-r--r-- | jstests/core/plan_cache_commands.js | 433 |
1 files changed, 0 insertions, 433 deletions
diff --git a/jstests/core/plan_cache_commands.js b/jstests/core/plan_cache_commands.js deleted file mode 100644 index 613d436aa15..00000000000 --- a/jstests/core/plan_cache_commands.js +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Plan cache commands - * - * Cache-wide Commands - * - planCacheListQueryShapes - * - planCacheClear - * Removes plans for one or all query shapes. - * - planCacheListPlans - */ - -var t = db.jstests_plan_cache_commands; -t.drop(); - -// Insert some data so we don't go to EOF. -t.save({a: 1, b: 1}); -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}); - -// Run the query. -var queryA1 = {a: 1, b:1}; -var projectionA1 = {_id: 0, a: 1}; -var sortA1 = {a: -1}; -assert.eq(1, t.find(queryA1, projectionA1).sort(sortA1).itcount(), 'unexpected document count'); -// We now expect the two indices to be compared and a cache entry to exist. - - -// -// tests for planCacheListQueryShapes -// Returns a list of query shapes for the queries currently cached in the collection. -// - -// Utility function to list query shapes in cache. -function getShapes(collection) { - if (collection == undefined) { - - collection = t; - } - var res = collection.runCommand('planCacheListQueryShapes'); - print('planCacheListQueryShapes() = ' + tojson(res)); - assert.commandWorked(res, 'planCacheListQueryShapes failed'); - assert(res.hasOwnProperty('shapes'), 'shapes missing from planCacheListQueryShapes result'); - return res.shapes; - -} - -// Attempting to retrieve cache information on non-existent collection is not an error -// and should return an empty array of query shapes. -var missingCollection = db.jstests_query_cache_missing; -missingCollection.drop(); -assert.eq(0, getShapes(missingCollection).length, - 'planCacheListQueryShapes should return empty array on non-existent collection'); - -// Retrieve query shapes from the test collection -// Number of shapes should match queries executed by multi-plan runner. -var shapes = getShapes(); -assert.eq(1, shapes.length, 'unexpected number of shapes in planCacheListQueryShapes result'); -assert.eq({query: queryA1, sort: sortA1, projection: projectionA1}, shapes[0], - 'unexpected query shape returned from planCacheListQueryShapes'); - - - -// -// Tests for planCacheClear (one query shape) -// - -// Invalid key should be a no-op. -t.runCommand('planCacheClear', {query: {unknownfield: 1}}); -assert.eq(1, getShapes().length, 'removing unknown query should not affecting exisiting entries'); - -// Run a new query shape and drop it from the cache -assert.eq(1, t.find({a: 2, b: 2}).itcount(), 'unexpected document count'); -assert.eq(2, getShapes().length, 'unexpected cache size after running 2nd query'); -assert.commandWorked(t.runCommand('planCacheClear', {query: {a: 1, b: 1}})); -assert.eq(1, getShapes().length, 'unexpected cache size after dropping 2nd query from cache'); - - - -// -// Tests for planCacheListPlans -// - -// 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; -} - -// Invalid key should be an error. -assert.eq(0, getPlans({unknownfield: 1}, {}, {}), - 'planCacheListPlans should return empty results on unknown query shape'); - -// Retrieve plans for valid cache entry. -var plans = getPlans(queryA1, sortA1, projectionA1); -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 planCacheClear -// - -// Drop query cache. This clears all cached queries in the collection. -res = t.runCommand('planCacheClear'); -print('planCacheClear() = ' + tojson(res)); -assert.commandWorked(res, 'planCacheClear failed'); -assert.eq(0, getShapes().length, 'plan cache should be empty after successful planCacheClear()'); - - - -// -// Query Plan Revision -// http://docs.mongodb.org/manual/core/query-plans/#query-plan-revision -// As collections change over time, the query optimizer deletes the query plan and re-evaluates -// after any of the following events: -// - The collection receives 1,000 write operations. -// - The reIndex rebuilds the index. -// - You add or drop an index. -// - The mongod process restarts. -// - -// Case 1: The collection receives 1,000 write operations. -// Steps: -// Populate cache. Cache should contain 1 key after running query. -// Insert 1000 documents. -// Cache should be cleared. -assert.eq(1, t.find(queryA1, projectionA1).sort(sortA1).itcount(), 'unexpected document count'); -assert.eq(1, getShapes().length, 'plan cache should not be empty after query'); -for (var i = 0; i < 1000; i++) { - t.save({b: i}); -} -assert.eq(0, getShapes().length, 'plan cache should be empty after adding 1000 documents.'); - -// Case 2: The reIndex rebuilds the index. -// Steps: -// Populate the cache with 1 entry. -// Run reIndex on the collection. -// Confirm that cache is empty. -assert.eq(1, t.find(queryA1, projectionA1).sort(sortA1).itcount(), 'unexpected document count'); -assert.eq(1, getShapes().length, 'plan cache should not be empty after query'); -res = t.reIndex(); -print('reIndex result = ' + tojson(res)); -assert.eq(0, getShapes().length, 'plan cache should be empty after reIndex operation'); - -// Case 3: You add or drop an index. -// Steps: -// Populate the cache with 1 entry. -// Add an index. -// Confirm that cache is empty. -assert.eq(1, t.find(queryA1, projectionA1).sort(sortA1).itcount(), 'unexpected document count'); -assert.eq(1, getShapes().length, 'plan cache should not be empty after query'); -t.ensureIndex({b: 1}); -assert.eq(0, getShapes().length, 'plan cache should be empty after adding index'); - -// Case 4: The mongod process restarts -// Not applicable. - - - -// -// 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; -var queryA3B3 = {a: 3, b: 3}; -for (var i = 0; i < numExecutions; i++) { - assert.eq(0, t.find(queryA3B3, projectionA1).sort(sortA1).itcount(), 'query failed'); -} - -plans = getPlans(queryA3B3, sortA1, projectionA1); - -// 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'); - } - assert(plans[i].reason.stats.hasOwnProperty('type'), 'no stats inserted for plan ' + i); -} - -// feedback meaningful only for plan 0 -// feedback is capped at 20 -// -// This assertion relies on the condition that the plan cache feedback mechanism -// has not evicted the cache entry. In order for this to be reliable, we must be -// sure that the plan scores the same each time it is run. We can be sure of this -// because: -// 1) The plan will produce zero results. This means that the productivity will -// always be zero, and in turn the score will always be the same. -// 2) The plan hits EOF quickly. This means that it will be cached despite -// returning zero results. -assert.eq(20, plans[0].feedback.nfeedback, 'incorrect nfeedback'); -assert.gt(plans[0].feedback.averageScore, 0, 'invalid average score'); - - - -// -// Tests for shell helpers -// - -// Reset collection data and indexes. -t.drop(); -var n = 200; -for (var i = 0; i < n; i++) { - t.save({a:i, b: i}); -} -t.ensureIndex({a: 1}); -t.ensureIndex({b: 1}); -t.ensureIndex({a: 1, b: 1}); - -// Repopulate plan cache with 3 query shapes. -var queryB = {a: {$gte: 0}, b: {$gte: 0}}; -var projectionB = {_id: 0, b: 1}; -var sortB = {b: -1}; -assert.eq(n, t.find(queryB, projectionB).sort(sortB).itcount(), 'unexpected document count'); -assert.eq(n, t.find(queryB, projectionB).itcount(), 'unexpected document count'); -assert.eq(n, t.find(queryB).sort(sortB).itcount(), 'unexpected document count'); -assert.eq(n, t.find(queryB).itcount(), 'unexpected document count'); -assert.eq(4, getShapes().length, 'unexpected number of query shapes in plan cache'); - -// -// PlanCache.getName -// - -var planCache = t.getPlanCache(); -assert.eq(t.getName(), planCache.getName(), 'name of plan cache should match collection'); - -// -// PlanCache.help -// -planCache.help(); - -// -// shellPrint -// - -print('plan cache:'); -print(planCache); - -// -// collection.getPlanCache().listQueryShapes -// - -missingCollection.drop(); -// should return empty array on non-existent collection. -assert.eq(0, missingCollection.getPlanCache().listQueryShapes().length, - 'collection.getPlanCache().listQueryShapes() should return empty results ' + - 'on non-existent collection'); -assert.eq(getShapes(), planCache.listQueryShapes(), - 'unexpected collection.getPlanCache().listQueryShapes() shell helper result'); - -// -// collection.getPlanCache().getPlansByQuery -// - -// should return empty array on non-existent query shape. -assert.eq(0, planCache.getPlansByQuery({unknownfield: 1}).length, - 'collection.getPlanCache().getPlansByQuery() should return empty results ' + - 'on non-existent collection'); -// should error on missing required field query. -assert.throws(function() { planCache.getPlansByQuery() }); - -// Invoke with various permutations of required (query) and optional (projection, sort) arguments. -assert.eq(getPlans(queryB, sortB, projectionB), planCache.getPlansByQuery(queryB, projectionB, - sortB), - 'plans from collection.getPlanCache().getPlansByQuery() different from command result'); -assert.eq(getPlans(queryB, {}, projectionB), planCache.getPlansByQuery(queryB, projectionB), - 'plans from collection.getPlanCache().getPlansByQuery() different from command result'); -assert.eq(getPlans(queryB, sortB, {}), planCache.getPlansByQuery(queryB, undefined, sortB), - 'plans from collection.getPlanCache().getPlansByQuery() different from command result'); -assert.eq(getPlans(queryB, {}, {}), planCache.getPlansByQuery(queryB), - 'plans from collection.getPlanCache().getPlansByQuery() different from command result'); - -// getPlansByQuery() will also accept a single argument with the query shape object -// as an alternative to specifying the query, sort and projection parameters separately. -// Format of query shape object: -// { -// query: <query>, -// projection: <projection>, -// sort: <sort> -// } -var shapeB = {query: queryB, projection: projectionB, sort: sortB}; -assert.eq(getPlans(queryB, sortB, projectionB), - planCache.getPlansByQuery(shapeB), - 'collection.getPlanCache().getPlansByQuery() did not accept query shape object'); - -// Should return empty array on missing or extra fields in query shape object. -// The entire invalid query shape object will be passed to the command -// as the 'query' component which will result in the server returning an empty -// array of plans. -assert.eq(0, planCache.getPlansByQuery({query: queryB}).length, - 'collection.getPlanCache.getPlansByQuery should return empty results on ' + - 'incomplete query shape'); -assert.eq(0, planCache.getPlansByQuery({query: queryB, sort: sortB, - projection: projectionB, - unknown_field: 1}).length, - 'collection.getPlanCache.getPlansByQuery should return empty results on ' + - 'invalid query shape'); - - - -// -// collection.getPlanCache().clearPlansByQuery -// - -// should not error on non-existent query shape. -planCache.clearPlansByQuery({unknownfield: 1}); -// should error on missing required field query. -assert.throws(function() { planCache.clearPlansByQuery() }); - -// Invoke with various permutations of required (query) and optional (projection, sort) arguments. -planCache.clearPlansByQuery(queryB, projectionB, sortB); -assert.eq(3, getShapes().length, - 'query shape not dropped after running collection.getPlanCache().clearPlansByQuery()'); - -planCache.clearPlansByQuery(queryB, projectionB); -assert.eq(2, getShapes().length, - 'query shape not dropped after running collection.getPlanCache().clearPlansByQuery()'); - -planCache.clearPlansByQuery(queryB, undefined, sortB); -assert.eq(1, getShapes().length, - 'query shape not dropped after running collection.getPlanCache().clearPlansByQuery()'); - -planCache.clearPlansByQuery(queryB); -assert.eq(0, getShapes().length, - 'query shape not dropped after running collection.getPlanCache().clearPlansByQuery()'); - -// clearPlansByQuery() will also accept a single argument with the query shape object -// as an alternative to specifying the query, sort and projection parameters separately. -// Format of query shape object: -// { -// query: <query>, -// projection: <projection>, -// sort: <sort> -// } - -// Repopulate cache -assert.eq(n, t.find(queryB, projectionB).sort(sortB).itcount(), 'unexpected document count'); - -// Clear using query shape object. -planCache.clearPlansByQuery(shapeB); -assert.eq(0, getShapes().length, - 'collection.getPlanCache().clearPlansByQuery() did not accept query shape object'); - -// Should not error on missing or extra fields in query shape object. -planCache.clearPlansByQuery({query: queryB}); -planCache.clearPlansByQuery({query: queryB, sort: sortB, projection: projectionB, - unknown_field: 1}); - - - -// -// collection.getPlanCache().clear -// - -// Should not error on non-existent collection. -missingCollection.getPlanCache().clear(); -// Re-populate plan cache with 1 query shape. -assert.eq(n, t.find(queryB, projectionB).sort(sortB).itcount(), 'unexpected document count'); -assert.eq(1, getShapes().length, 'plan cache should not be empty after running cacheable query'); -// Clear cache. -planCache.clear(); -assert.eq(0, getShapes().length, 'plan cache not empty after clearing'); - - - -// -// explain and plan cache -// Running explain should not mutate the plan cache. -// - -planCache.clear(); - -// MultiPlanRunner explain -var multiPlanRunnerExplain = t.find(queryB, projectionB).sort(sortB).explain(true); - -print('multi plan runner explain = ' + tojson(multiPlanRunnerExplain)); - -assert.eq(0, getShapes().length, 'explain should not mutate plan cache'); - - - - -// -// SERVER-12796: Plans for queries that return zero -// results should not be cached. -// - -t.drop(); - -t.ensureIndex({a: 1}); -t.ensureIndex({b: 1}); - -for (var i = 0; i < 200; i++) { - t.save({a: 1, b: 1}); -} -t.save({a: 2, b: 2}); - -// A query with zero results that does not hit EOF should not be cached... -assert.eq(0, t.find({c: 0}).itcount(), 'unexpected count'); -assert.eq(0, getShapes().length, 'unexpected number of query shapes in plan cache'); - -// ...but a query with zero results that hits EOF will be cached. -assert.eq(0, t.find({a: 3, b: 3}).itcount(), 'unexpected count'); -assert.eq(1, getShapes().length, 'unexpected number of query shapes in plan cache'); - -// A query that returns results but does not hit EOF will also be cached. -assert.eq(200, t.find({a: {$gte: 0}, b:1}).itcount(), 'unexpected count'); -assert.eq(2, getShapes().length, 'unexpected number of query shapes in plan cache'); |