Files
mongo/jstests/libs/plan_cache_utils.js
2024-03-25 17:31:35 +00:00

92 lines
3.5 KiB
JavaScript

/**
* Utility methods for reading planCache counters
*/
import {getCachedPlan, getPlanStage} from "jstests/libs/analyze_plan.js";
import {getLatestProfilerEntry} from "jstests/libs/profiler.js";
export function getPlanCacheSize(db) {
return db.serverStatus().metrics.query.planCache.totalSizeEstimateBytes;
}
export function getPlanCacheNumEntries(db) {
return db.serverStatus().metrics.query.planCache.totalQueryShapes;
}
function getPlansForCacheEntry(coll, match) {
const matchingCacheEntries = coll.getPlanCache().list([{$match: match}]);
assert.eq(matchingCacheEntries.length, 1, coll.getPlanCache().list());
return matchingCacheEntries[0];
}
function planHasIxScanStageForIndex(planStats, indexName) {
const stage = getPlanStage(planStats, "IXSCAN");
return (stage === null) ? false : indexName === stage.indexName;
}
export function assertCacheUsage(coll,
pipeline,
fromMultiPlanning,
cacheEntryVersion,
cacheEntryIsActive,
cachedIndexName,
aggOptions = {}) {
const profileObj = getLatestProfilerEntry(
coll.getDB(), {op: {$in: ["command", "getmore"]}, ns: coll.getFullName()});
const queryHash = profileObj.queryHash;
const planCacheKey = profileObj.planCacheKey;
assert.eq(fromMultiPlanning, !!profileObj.fromMultiPlanner, profileObj);
const entry = getPlansForCacheEntry(coll, {queryHash: queryHash});
assert.eq(cacheEntryVersion, entry.version, entry);
assert.eq(cacheEntryIsActive, entry.isActive, entry);
// If the entry is active, we should have a plan cache key.
if (entry.isActive) {
assert(entry.planCacheKey);
}
if (planCacheKey) {
assert.eq(entry.planCacheKey, planCacheKey);
const explain = coll.explain().aggregate(pipeline, aggOptions);
const explainKey = explain.hasOwnProperty("queryPlanner")
? explain.queryPlanner.planCacheKey
: explain.stages[0].$cursor.queryPlanner.planCacheKey;
assert.eq(explainKey, entry.planCacheKey);
}
if (cacheEntryVersion === 2) {
assert(entry.cachedPlan.stages.includes(cachedIndexName), entry);
} else {
assert(planHasIxScanStageForIndex(getCachedPlan(entry.cachedPlan), cachedIndexName), entry);
}
}
export function setUpActiveCacheEntry(
coll, pipeline, cacheEntryVersion, cachedIndexName, assertFn) {
// For the first run, the query should go through multiplanning and create inactive cache entry.
assertFn(coll.aggregate(pipeline));
assertCacheUsage(coll,
pipeline,
true /*multiPlanning*/,
cacheEntryVersion,
false /*cacheEntryIsActive*/,
cachedIndexName);
// After the second run, the inactive cache entry should be promoted to an active entry.
assertFn(coll.aggregate(pipeline));
assertCacheUsage(coll,
pipeline,
true /*multiPlanning*/,
cacheEntryVersion,
true /*cacheEntryIsActive*/,
cachedIndexName);
// For the third run, the active cached query should be used.
assertFn(coll.aggregate(pipeline));
assertCacheUsage(coll,
pipeline,
false /*multiPlanning*/,
cacheEntryVersion,
true /*cacheEntryIsActive*/,
cachedIndexName);
}