Conversation
This was referenced Oct 3, 2026
zzylol
marked this pull request as draft
October 3, 2026 19:29
Contributor
Author
|
Parked as draft: PR priorities changed (see #528). Order is now (A) finish #511 operator sharing, (B) the #572 crate/module reorganization, (C) #509 end-to-end stages. This PR sits on the old 🤖 Generated with Claude Code |
zzylol
added a commit
that referenced
this pull request
Oct 4, 2026
A tumbling pane (#580) reads TimeRange(w) over TimeShift(i*w) over Scan. The per-entity build path required the Scan directly under the TimeRange and rejected panes with "per-entity summary requires a resolved source". The deployment supplies the shifted raw rows, so look through one TimeShift to find the source schema. Ported from #566. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem: the #509 window-composition rule and the Example 4 materialization choices are not generated automatically
#509 §Pass 2: ASAP-aware common-subexpression elimination defines the window-composition rule: computations with the same summary input data can share "one window summary feeding per-query merge (where needed) and estimation nodes". For tumbling windows it says:
#509 §2 Materialization then requires stage 2 to give each sub-DAG the options "materialized at ingestion time", "materialized at query time" and "not materialized". §Example 4, Pattern B lists them for the same 5-min KLL: B1 (store 1-min panes from ingestion), B2 (rebuild all 5 panes at every query), B3 (keep panes built at query time).
Before this PR, neither step was generated by the planner. For #509 §Example 3 Pattern B:
Pass 1 produces one summary over the whole lookback:
SummaryMerge(feat(ir): define compatible logical summary merges #560) and native merge exist, but a caller had to build the five 1-min pane subtrees and the merge by hand. No rule derived the pane width from the workload's cadence.enumerate_frontiersandcompile_candidatesexisted as two separate calls. Each lowered the DAG again, and the caller had to pair each frontier with its result.SummaryAggwithreduction: PerEntity, compilation requiredTimeRangedirectly overScan. A pane isTimeRangeoverTimeShiftoverScan, so a per-entity pane was rejected withper-entity summary requires a resolved source. (The KLL example above is not per-entity and did not hit this check.)Scope covered here. The tumbling-window part of the window-composition rule, as an exact logical alternative per summary, and automatic enumeration of every legal producer/reader split of the lowered DAG (the Example 4 Pattern B options B1/B2/B3).
Left out. Sliding windows and Exponential Histograms. A rotating cache that keeps panes across evaluations. Historical EH boundary certificates. Workload-level selection over these candidates (#568). Temporal merge timestamp binding (#569). The panes do not carry #567
SummaryCoverage: this branch is on the #541 line, which predates it.Proposed method
Two independent pieces.
1. Logical:
enumerate_window_compositions(Pass 2,asap-aware-mapping).entry.recurrence:Repeated(FixedInterval(i))orRepeated(FixedIntervalAt { interval: i, .. })→i(the phase is not used).Repeated(Scheduled(times))→ GCD of the differences between consecutive times.OneTime,Unknown,EstimatedRate) →0.limit == 0, returnBudgetExceeded. If the cadence is0, return only the original root.compose(node, cadence, limit). A node is a composition site when it is:SummaryAggwhose child isTimeRange { range, kind },rangeis a whole, nonzero number of milliseconds (lookback),TimeRangeis aScanofSource::TimeSeries, or aTimeShiftdirectly over such aScan.width = gcd(lookback, cadence), andcount = lookback / width. This is the largest width that divides both the window and the evaluation interval, as docs: propose workload-wide planning, summary sharing, and materialization #509 requires. Ifcount <= 1there is nothing to compose. Ifcount > limit, returnBudgetExceeded.countpanes. Paneiis a copy of the originalSummaryAgg(same family, input, reduction, grouping, filter) whose input isTimeRange(width, same kind)overTimeShift { offset_ms: shift.offset_ms + i·width, at: shift.at }over the sameScan. Pane 0 is the most recentwidth; panecount−1is the oldest. The panes are disjoint and together cover exactly the original[t − lookback, t)(plus any original offset/anchor).SummaryMerge. IfSummaryMergeconstruction fails (for example, incompatible state schemas), the site is skipped. The merge node takes the original node'sschemaandguarantee.nsites,2^ncandidates (bitkof the mask = use the composed form at sitek). Mask 0 is the original root, unchanged. If2^n > limit, returnBudgetExceededand no partial list. Ancestors of a replaced node are rebuilt withmap_childrenand keep theirguarantee.This is candidate generation only. It does no costing and does not check the family's merge capability; physical compilation does that.
2. Physical:
compile_materialization_candidates(stage 2,asap-physical-operators).PostAsapDAGonce (compile).max_candidates, return an error and no partial list.cut_candidateto split the DAG into aprecomputepart (produces the frontier outputs) and aquerypart (reads them). A failed cut is kept asErrin that candidate. It does not fail the whole call.The physical compiler also now accepts
TimeRange → TimeShift → Scanunder aPerEntitysummary, so generated per-entity panes lower.Key code interfaces
crates/asap-aware-mapping/src/window_composition.rs(new public modulewindow_composition):crates/asap-physical-operators/src/physical_planner/candidates.rs(re-exported fromphysical_planner):Existing type it returns (unchanged):
Usage (from
crates/integration-tests/tests/automatic_window_composition.rs):Fields
enumerate_window_compositionsroot&Rc<OperatorNode>SummaryAggis a possible site.entry&QueryWorkloadEntryrecurrenceis read, to get the cadence (see method step 1).limitusize2^sites).Result<Vec<Rc<OperatorNode>>, _>rootitself (Rc::ptr_eq). Unknown cadence or no sites →vec![root].WindowCompositionErrorBudgetExceededlimit == 0; a site needs more thanlimitpanes; or2^sites > limit. No partial list is returned.Schema(SchemaDerivationError)TimeShift/TimeRangenode, or rebuilding a parent, failed schema derivation.Pane construction (internal
compose)lookbackTimeRange.rangein mscadenceentry.recurrencewidthgcd(lookback, cadence)countlookback / widthTimeShift.offset_msshift.offset_ms + i·widthiends. Positive = earlier. The original offset is added.TimeShift.atat@anchor kept as is.TimeRange.kindkindMaterializationCandidatefrontierVec<NodeId>(u64)precomputeand stored. No two are ancestor/descendant. Empty = nothing stored, everything runs at query time.realizationResult<PhysicalASAPDAG, Error>compile_materialization_candidatesdag&PostAsapDAGinputsBTreeMap<NodeId, InputContract>TimeRange.roots&[NodeId]max_candidatesusize0or too many frontiers →Err.Result<Vec<MaterializationCandidate>, Error>Erronly if lowering fails or the budget is exceeded.PhysicalASAPDAG(existing)precomputeOption<CompiledPhysicalDAG>Nonefor the empty frontier.queryCompiledPhysicalDAGmaterialized_outputsBTreeMap<NodeId, InputContract>queryreads.Examples
End-to-end: #509 Example 3/4 Pattern B (
crates/integration-tests/tests/automatic_window_composition.rs).Input:
SummaryAgg(KLL k=200)overTimeRange(300 s)overScan(TimeSeries "events"); recurrenceFixedInterval(60_000).enumerate_window_compositions(&state, &entry, 64)returns 2 candidates: the original, andA
SummaryEstimate { Quantile { q: 0.99 } }is put on top and compiled to wire form. Each pane'sTimeRangegets 20 rows: panepgets valuesp·20 … p·20+19, so the panes together hold0 … 99.compile_materialization_candidates(.., 1024)succeeds. Withmax_candidates = 1it returnsErr.Empty frontier (B2: rebuild all panes per query): query result
98.0.Frontier = the five pane
SummaryAggnodes (B1/B3: store panes):precomputeruns inScope::Ingestion { 0 .. 300_000, revision 1 }, its outputs feedqueryinScope::Query { evaluation_time_ms: 300_000, revision 1 }. Result:98.0, the same as step 4.Pane width for other cadences (from
width = gcd(lookback, cadence)):count <= 1)OneTime)Accepted vs rejected (unit tests in
window_composition.rs):five_minute_window_has_original_and_five_exact_panes: 5 min / 60 s,limit = 160, 60 000, …, 240 000; merge passesvalidate_structureunknown_cadence_and_budget_are_explicit: same,limit = 4BudgetExceeded(5 panes > 4)OneTime { invocations: 1 },limit = 4SummaryAggover a non-time-series source, or over other operators betweenTimeRangeandScanOut of scope
FixedIntervalAt's phase is not used.docs/develop_docs/planner-layering-status.mdupdates the "Window composition" row and adds an "Automatic window/materialization acceptance" section.Stack and validation
Stacked on #565 (
stack/509-20-sql-entropy-fallback). Head:stack/509-21-window-composition. Next: #568 (complete workload selection), then #569 (temporal merge timestamp binding). Implements the exact window/materialization portion of #509.Validation (from the current PR body): focused window tests, the native end-to-end direct/materialized quantile fixture, targeted all-feature Clippy, formatting and diff checks pass.
🤖 Generated with Claude Code