diff options
Diffstat (limited to 'jstests/core/timeseries/timeseries_lastpoint.js')
-rw-r--r-- | jstests/core/timeseries/timeseries_lastpoint.js | 214 |
1 files changed, 58 insertions, 156 deletions
diff --git a/jstests/core/timeseries/timeseries_lastpoint.js b/jstests/core/timeseries/timeseries_lastpoint.js index 3be28645c7b..05344c194da 100644 --- a/jstests/core/timeseries/timeseries_lastpoint.js +++ b/jstests/core/timeseries/timeseries_lastpoint.js @@ -29,173 +29,75 @@ if (!isLastpointEnabled) { return; } -// Timeseries test parameters. -const numHosts = 10; -const numIterations = 20; - -function verifyTsResults({pipeline, precedingFilter, expectStage, prepareTest}) { - // Prepare collections. Note: we test without idle measurements (all meta subfields are - // non-null). If we allow the insertion of idle measurements, we will obtain multiple lastpoints - // per bucket, and may have different results on the observer and timeseries collections. - const [tsColl, observerColl] = TimeseriesAggTests.prepareInputCollections( - numHosts, numIterations, false /* includeIdleMeasurements */); - - // Additional preparation before running the test. - if (prepareTest) { - prepareTest(tsColl, observerColl); +function verifyTsResults(pipeline, index, precedingFilter) { + // Prepare collections. + const numHosts = 10; + const numIterations = 20; + const [tsColl, observerColl] = + TimeseriesAggTests.prepareInputCollections(numHosts, numIterations); + if (index) { + tsColl.createIndex(index); } // Verify lastpoint optmization. const explain = tsColl.explain().aggregate(pipeline); - expectStage({explain, precedingFilter}); - - // Assert that the time-series aggregation results match that of the observer collection. - const expected = observerColl.aggregate(pipeline).toArray(); - const actual = tsColl.aggregate(pipeline).toArray(); - assertArrayEq({actual, expected}); - - // Drop collections. - tsColl.drop(); - observerColl.drop(); -} - -function verifyTsResultsWithAndWithoutIndex( - {pipeline, index, bucketsIndex, precedingFilter, expectStage, prePrepareTest}) { - verifyTsResults( - {pipeline, precedingFilter, expectStage: expectCollScan, prepareTest: prePrepareTest}); - verifyTsResults({ - pipeline, - precedingFilter, - expectStage, - prepareTest: (testColl, observerColl) => { - // Optionally do extra test preparation. - if (prePrepareTest) { - prePrepareTest(testColl, observerColl); - } - - // Create index on the timeseries collection. - testColl.createIndex(index); - - // Create an additional secondary index directly on the buckets collection so that we - // can test the DISTINCT_SCAN optimization when time is sorted in ascending order. - if (bucketsIndex) { - const bucketsColl = testDB["system.buckets.in"]; - bucketsColl.createIndex(bucketsIndex); - } + if (index) { + // The query can utilize DISTINCT_SCAN. + assert.neq(getAggPlanStage(explain, "DISTINCT_SCAN"), null, explain); + + // Pipelines that use the DISTINCT_SCAN optimization should not also have a blocking sort. + assert.eq(getAggPlanStage(explain, "SORT"), null, explain); + } else { + // $sort can be pushed into the cursor layer. + assert.neq(getAggPlanStage(explain, "SORT"), null, explain); + + // At the bottom, there should be a COLLSCAN. + const collScanStage = getAggPlanStage(explain, "COLLSCAN"); + assert.neq(collScanStage, null, explain); + if (precedingFilter) { + assert.eq(precedingFilter, collScanStage.filter, collScanStage); } - }); -} - -function expectDistinctScan({explain}) { - // The query can utilize DISTINCT_SCAN. - assert.neq(getAggPlanStage(explain, "DISTINCT_SCAN"), null, explain); - - // Pipelines that use the DISTINCT_SCAN optimization should not also have a blocking sort. - assert.eq(getAggPlanStage(explain, "SORT"), null, explain); -} - -function expectCollScan({explain, precedingFilter}) { - // $sort can be pushed into the cursor layer. - assert.neq(getAggPlanStage(explain, "SORT"), null, explain); - - // At the bottom, there should be a COLLSCAN. - const collScanStage = getAggPlanStage(explain, "COLLSCAN"); - assert.neq(collScanStage, null, explain); - if (precedingFilter) { - assert.eq(precedingFilter, collScanStage.filter, collScanStage); } -} - -function expectIxscan({explain}) { - // $sort can be pushed into the cursor layer. - assert.neq(getAggPlanStage(explain, "SORT"), null, explain); - // At the bottom, there should be a IXSCAN. - assert.neq(getAggPlanStage(explain, "IXSCAN"), null, explain); + // Assert that the time-series aggregation results match that of the observer collection. + const expectedResults = observerColl.aggregate(pipeline).toArray(); + const actualResults = tsColl.aggregate(pipeline).toArray(); + assert(resultsEq(actualResults, expectedResults), + `Expected ${tojson(expectedResults)} but got ${tojson(actualResults)}`); } -function getGroupStage(accumulator) { - return { - $group: { - _id: "$tags.hostid", - usage_user: {[accumulator]: "$usage_user"}, - usage_guest: {[accumulator]: "$usage_guest"}, - usage_idle: {[accumulator]: "$usage_idle"} - } - }; +function verifyTsResultsWithAndWithoutIndex(pipeline, index, precedingFilter) { + verifyTsResults(pipeline, undefined, precedingFilter); + verifyTsResults(pipeline, index, precedingFilter); } -/** - Test cases: - 1. Lastpoint queries on indexes with descending time and $first (DISTINCT_SCAN). - 2. Lastpoint queries on indexes with ascending time and $last (no DISTINCT_SCAN). - 3. Lastpoint queries on indexes with ascending time and $last and an additional secondary - index so that we can use the DISTINCT_SCAN optimization. -*/ -const testCases = [ - {time: -1}, - {time: 1}, - {time: -1, bucketsIndex: {"meta.hostid": -1, "control.max.time": 1, "control.min.time": 1}} -]; - -for (const {time, bucketsIndex} of testCases) { - const isTimeDescending = time < 0; - const canUseDistinct = isTimeDescending || bucketsIndex; - const groupStage = isTimeDescending ? getGroupStage("$first") : getGroupStage("$last"); - - // Test both directions of the metaField sort for each direction of time. - for (const index of [{"tags.hostid": 1, time}, {"tags.hostid": -1, time}]) { - // Test pipeline without a preceding $match stage. - verifyTsResultsWithAndWithoutIndex({ - pipeline: [{$sort: index}, groupStage], - index, - bucketsIndex, - expectStage: (canUseDistinct ? expectDistinctScan : expectCollScan) - }); - - // Test pipeline without a preceding $match stage which has an extra idle measurement. This - // verifies that the query rewrite correctly returns missing fields. - verifyTsResultsWithAndWithoutIndex({ - pipeline: [{$sort: index}, groupStage], - index, - bucketsIndex, - expectStage: (canUseDistinct ? expectDistinctScan : expectCollScan), - prePrepareTest: (testColl, observerColl) => { - const currTime = new Date(); - for (const host of TimeseriesTest.generateHosts(numHosts)) { - const idleMeasurement = { - tags: host.tags, - time: new Date(currTime + numIterations), // Ensure this is the lastpoint. - idle_user: 100 - TimeseriesTest.getRandomUsage() - }; - assert.commandWorked(testColl.insert(idleMeasurement)); - assert.commandWorked(observerColl.insert(idleMeasurement)); - } +verifyTsResultsWithAndWithoutIndex( + [ + {$sort: {"tags.hostid": 1, time: -1}}, + { + $group: { + _id: "$tags.hostid", + usage_user: {$first: "$usage_user"}, + usage_guest: {$first: "$usage_guest"}, + usage_idle: {$first: "$usage_idle"} } - }); - - // Test pipeline with a preceding $match stage. - function testWithMatch(matchStage, precedingFilter) { - verifyTsResultsWithAndWithoutIndex({ - pipeline: [matchStage, {$sort: index}, groupStage], - index, - bucketsIndex, - expectStage: canUseDistinct ? expectDistinctScan : expectIxscan, - precedingFilter - }); } - - // Test pipeline with an equality $match stage. - testWithMatch({$match: {"tags.hostid": 0}}, {"meta.hostid": {$eq: 0}}); - - // Test pipeline with an inequality $match stage. - testWithMatch({$match: {"tags.hostid": {$ne: 0}}}, {"meta.hostid": {$not: {$eq: 0}}}); - - // Test pipeline with a $match stage that uses a $gt query. - testWithMatch({$match: {"tags.hostid": {$gt: 5}}}, {"meta.hostid": {$gt: 5}}); - - // Test pipeline with a $match stage that uses a $lt query. - testWithMatch({$match: {"tags.hostid": {$lt: 5}}}, {"meta.hostid": {$lt: 5}}); - } -} + ], + {"tags.hostid": 1, time: -1}); + +verifyTsResultsWithAndWithoutIndex( + [ + {$match: {"tags.hostid": "host_0"}}, + {$sort: {"tags.hostid": 1, time: -1}}, + { + $group: { + _id: "$tags.hostid", + usage_user: {$first: "$usage_user"}, + usage_guest: {$first: "$usage_guest"}, + usage_idle: {$first: "$usage_idle"} + } + } + ], + {"tags.hostid": 1, time: -1}, + {"meta.hostid": {$eq: "host_0"}}); })(); |