fix(schema-compiler): only build pre-agg partitions needed for bounded rolling-window queries#11277
Conversation
…d rolling-window queries
|
Claude finished @igorlukanin's task in 1m 13s —— View job Code reviewDetails
Overall the fix is well-scoped and the reasoning in the PR description matches the code: A few observations worth considering — none blocking: 1. 2. Degenerate rolling window with no 3. 4. Comparing intervals by 5. Unbounded-check flow ( 6. Test coverage is tight and exactly targets the regression, which is great. One additional case that would harden it: a measure with Nothing here changes the correctness verdict — the fix looks right. LGTM modulo the union-comparison note. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #11277 +/- ##
===========================================
- Coverage 79.43% 59.44% -19.99%
===========================================
Files 473 216 -257
Lines 97382 17509 -79873
Branches 3539 3547 +8
===========================================
- Hits 77354 10409 -66945
+ Misses 19522 6592 -12930
- Partials 506 508 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Claude finished @igorlukanin's task in 1m 23s —— View job Code review (updated for commit
|
…ry timestamp precision
|
Claude finished @igorlukanin's task in 1m 52s —— View job Code review (updated for commit
|
|
Follow-up: my first cut regressed the I had added a Reverted that union — the usage-info path is now identical to |
| if (seconds > maxTrailingSeconds) { | ||
| maxTrailingSeconds = seconds; | ||
| maxTrailing = parsed; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Minor: the rollingWindow.leading === 'unbounded' case in the third if is unreachable — the preceding if (rollingWindow.leading && rollingWindow.leading !== 'unbounded') returns null for every truthy non-'unbounded' leading, and the only remaining truthy value is 'unbounded' itself. The two ifs collapse to one without behavior change:
if (rollingWindow.leading || rollingWindow.trailing === 'unbounded') {
return null;
}Not a bug, just dead code.
| * | ||
| * Returns `null` when narrowing is not safe (an unbounded trailing or any leading | ||
| * window is present) — the caller then keeps the previous behaviour of building | ||
| * the whole build range. Returns `undefined` when there are no cumulative measures. |
There was a problem hiding this comment.
Low: cumulativeMeasuresTrailingInterval() walks the leaf-measure graph on every preAggregationDescriptionFor call. hasCumulativeMeasures() right above is memoized (hasCumulativeMeasuresValue) for the same reason — worth caching this too, especially for queries touching many rollupJoin/rollupLambda references.
Issue
CORE-164 — a query with a narrow
dateRange(e.g.yesterday) against a week-partitioned pre-aggregation builds all partitions instead of only the one(s) needed to serve the timeframe.Root cause
preAggregationDescriptionForgated computingmatchedTimeDimensionDateRangeon!this.hasCumulativeMeasures(). So whenever the query has a cumulative (rolling window) measure,matchedTimeDimensionDateRangewas leftundefined, and the orchestrator'sPreAggregationPartitionRangeLoader.partitionRanges()→intersectDateRanges(buildRange, undefined)falls back to the whole build range — every partition.That blanket skip is only correct for unbounded trailing windows (which genuinely need all history). For a bounded window (
trailing: 7 day) only[queryStart − window, queryEnd]is needed.Evidence (local repro)
Week-partitioned pre-agg,
dateRange= a single day, ~4.5 years of build range:matchedTimeDimensionDateRangerollingWindow.trailing: 7 dayrollingWindow.trailing: unboundedA correct 7-day-trailing build for a single day needs 2 weekly partitions, not 235.
Fix
cumulativeMeasuresTrailingInterval()classifies the query's cumulative leaf measures:fixedwindow → don't narrow, keep building the whole range (previous behaviour preserved).The usage-info merge path (Tesseract) now takes the union of the per-usage range and the base matched range, so neither a
time_shiftusage's earlier partitions nor a rolling window's trailing lookback is dropped.Tesseract has no partition-narrowing logic of its own — it routes pre-agg description assembly through this same JS method — so the fix covers both the default (Tesseract) and legacy planners.
Tests
New
core-164-rolling-window-partitions.test.ts:matchedTimeDimensionDateRangeset to the requested range expanded back by the window (wasundefinedbefore — red without the fix);undefined(whole range), guarding the correct case.Existing
pre-aggregations,pre-agg-time-dim-match,pre-agg-by-filter-matchsuites: no new failures (the pre-existing failures in this checkout are native-binary/viewGroupsdrift, unrelated and identical onmaster).