mongo/jstests/aggregation/optimization/is_count_like.js

68 lines
2.0 KiB
JavaScript

/**
* Checks that caching a query with predicate X, followed by a similar query with X in a rooted $or
* does not pass along the isCountLike attribute to the subquery. Previously we would always pass
* isCountLike to the child, meaning we could retrieve an incorrect plan from the cache and tassert.
*
* @tags: [
* # If all chunks are moved off of a shard, it can cause the plan cache to miss commands.
* assumes_balancer_off,
* does_not_support_stepdowns,
* assumes_unsharded_collection,
* not_allowed_with_signed_security_token,
* ]
*/
const collName = "is_count_like";
const coll = db[collName];
coll.drop();
// Creating this index allows an isCountLike index plan to be turned into a COUNT_SCAN, reproducing
// the issue.
coll.createIndex({a: 1});
coll.createIndex({b: 1});
coll.createIndex({a: 1, b: 1});
coll.insert({a: 0, b: 0});
function testConsecutiveQueriesWork(firstQuery, secondQuery) {
// Run each query enough to get them cached. Then swap the order, to make sure there are no
// failures if the second query is cached first.
coll.getPlanCache().clear();
for (let i = 0; i < 5; i++) {
firstQuery();
}
for (let i = 0; i < 5; i++) {
secondQuery();
}
coll.getPlanCache().clear();
for (let i = 0; i < 5; i++) {
secondQuery();
}
for (let i = 0; i < 5; i++) {
firstQuery();
}
}
function testCountLikeStage(stage) {
testConsecutiveQueriesWork(
function () {
coll.aggregate([{$match: {a: 0}}, stage]).toArray();
},
function () {
coll.aggregate([{$match: {$or: [{a: 0}, {b: 0}]}}, stage]).toArray();
},
);
}
testConsecutiveQueriesWork(
function () {
coll.find({a: 0}).count();
},
function () {
coll.find({$or: [{a: 0}, {b: 0}]}).count();
},
);
// Replacing root with {} and $count are both count-like as well.
testCountLikeStage({$replaceRoot: {newRoot: {}}});
testCountLikeStage({$count: "c"});