Files
mongo/jstests/core/query/partial_index_pbt.js
Joshua Siegel 169f8dc283 SERVER-111817 Add tag to exclude tests from timeseries CRUD suite (#44113)
GitOrigin-RevId: a3ab3b89275fa89c9824f6af467d56d605096159
2025-11-21 18:08:16 +00:00

89 lines
4.5 KiB
JavaScript

/**
* A property-based test to assert the correctness of partial indexes. Generates a filter, indexes,
* and queries, then creates partial indexes using the filter and prefixes every query with
* {$match: filter}. This way, every query is eligible to use the indexes, rather than leaving it
* up to chance.
* Queries with similar shapes are run consecutively, to trigger plan cache interactions.
*
* @tags: [
* query_intensive_pbt,
* # This test runs commands that are not allowed with security token: setParameter.
* not_allowed_with_signed_security_token,
* assumes_no_implicit_collection_creation_after_drop,
* # Incompatible with setParameter
* does_not_support_stepdowns,
* # Change in read concern can slow down queries enough to hit a timeout.
* assumes_read_concern_unchanged,
* does_not_support_causal_consistency,
* # Runs queries that may return many results, requiring getmores
* requires_getmore,
* multiversion_incompatible,
* # Time series collections do not support indexing array values in measurement fields.
* exclude_from_timeseries_crud_passthrough,
* ]
*/
import {createCacheCorrectnessProperty} from "jstests/libs/property_test_helpers/common_properties.js";
import {getDatasetModel} from "jstests/libs/property_test_helpers/models/document_models.js";
import {getIndexModel} from "jstests/libs/property_test_helpers/models/index_models.js";
import {getPartialFilterPredicateArb} from "jstests/libs/property_test_helpers/models/match_models.js";
import {getQueryAndOptionsModel} from "jstests/libs/property_test_helpers/models/query_models.js";
import {partialIndexCounterexamples} from "jstests/libs/property_test_helpers/pbt_resolved_bugs.js";
import {concreteQueryFromFamily, testProperty} from "jstests/libs/property_test_helpers/property_testing_utils.js";
import {isSlowBuild} from "jstests/libs/query/aggregation_pipeline_utils.js";
import {fc} from "jstests/third_party/fast_check/fc-3.1.0.js";
if (isSlowBuild(db)) {
jsTestLog("Exiting early because debug is on, opt is off, or a sanitizer is enabled.");
quit();
}
const numRuns = 100;
const numQueriesPerRun = 20;
const controlColl = db.partial_index_pbt_control;
const experimentColl = db.partial_index_pbt_experiment;
// Use the cache correctness property so we can test interactions between the plan cache and
// partial indexes.
const correctnessProperty = createCacheCorrectnessProperty(controlColl, experimentColl);
const workloadModel = fc
.record({
// This filter will be used for the partial index filter, and to prefix queries with
// {$match: filter} so that every query is eligible to use the partial indexes.
partialFilterPredShape: getPartialFilterPredicateArb(),
docs: getDatasetModel(),
indexes: fc.array(getIndexModel({allowPartialIndexes: false, allowSparse: false}), {
minLength: 0,
maxLength: 15,
size: "+2",
}),
queries: fc.array(getQueryAndOptionsModel(), {minLength: 1, maxLength: numQueriesPerRun, size: "+2"}),
})
.map(({partialFilterPredShape, docs, indexes, queries}) => {
// The predicate model generates a family of predicates of the same shape, with different
// parameter options at the leaf nodes. For all indexes, we use the first predicate from the
// family as the partial filter expression.
const firstPartialFilterPred = concreteQueryFromFamily(partialFilterPredShape, 0);
const partialIndexes = indexes.map(({def, options}) => {
return {
def,
options: Object.assign({}, options, {partialFilterExpression: firstPartialFilterPred}),
};
});
// For queries, we can include the entire predicate family in the $match. When the property
// asks for similar query shapes with different parameters plugged in, the $match will
// behave correctly. In general our queries are modeled as families of shapes, so including
// the predicate family in the $match rather than one specific predicate makes sense.
const match = {$match: partialFilterPredShape};
const queriesWithMatch = queries.map((query) => ({
"pipeline": [match, ...query.pipeline],
"options": query.options,
}));
return {collSpec: {isTS: false, docs, indexes: partialIndexes}, queries: queriesWithMatch};
});
// Test with a regular collection.
testProperty(correctnessProperty, {controlColl, experimentColl}, workloadModel, numRuns, partialIndexCounterexamples);
// TODO SERVER-103381 extend this test to use time-series collections.