# v0.89.0 — Incremental Window Functions > **Status:** Planned > **Scope:** Large > **User promise:** *"One PostgreSQL instance can handle a lot."* > **Blocked by:** [v0.88.0](v0.88.0.md) > **Split from:** the original v0.84.0 "Fast Incremental Engine". ## Theme Establish a correct, bounded, crash-safe framework for incremental window maintenance, then admit only the function and frame combinations that show a measured benefit over partition recomputation. This is a release on its own, not a bullet point. Every entry in the table below is a distinct algorithm with its own auxiliary state, its own persistence and crash-recovery story, its own migration path for existing stream tables, and its own DIFFERENTIAL-versus-FULL proof obligation. Bundling it with the vectorized aggregate path and a delta-query cost model — as the original v0.84.0 did — produced a release that could not be scoped, reviewed or gated. The state framework is mandatory. Algorithm breadth is not a v1.0 blocker. Any shape that does not pass the admission gate keeps the existing, correct partition-recomputation path with a visible reason code. ## Non-Goals - No new window functions beyond those PostgreSQL already supports. - No change to the window semantics users see. Only the maintenance strategy changes. - No removal of the partition-recomputation path — it remains the fallback. - No requirement that every candidate family become incremental before v1.0. ## Items ### LT-7a: Incremental state model and persistence Before any individual algorithm, define once: - where per-partition auxiliary state lives (a side table keyed by stream table and partition key, not user-visible columns) - how it is initialised on first refresh and rebuilt on reinitialisation - how it survives crash, restore and `ALTER`, and how a mismatch is detected - how it is bounded, so a query with millions of partitions cannot grow state without a ceiling and an alert (bound sourced from `pg_trickle.memory_budget_mb`, v0.87.0) New metadata in `pgt_stream_tables`: `window_strategy JSONB` recording, per window function, the chosen strategy and its auxiliary state location. ### LT-7b: Algorithm admission gate A function and frame combination becomes incremental only when it passes all of these checks: 1. PostgreSQL semantics match exactly, including peers, NULL ordering, collations, and frames. 2. DIFFERENTIAL converges with FULL after INSERT, UPDATE, and DELETE histories. 3. The auxiliary state survives restart, backup, restore, rebuild, and upgrade. 4. State growth is bounded under the documented policy. 5. Output amplification and the recomputation crossover are measured. 6. A representative end-to-end workload shows a material speedup without exceeding its regression budget. Failure of any check selects partition recomputation. The support matrix and diagnostics report the reason. ### LT-7c: Rank-family candidates | Function | Current strategy | Admission target | |----------|------------------|------------------| | `ROW_NUMBER()` | Recompute partition | Update ordered per-partition state when cheaper than recomputation | | `RANK()` / `DENSE_RANK()` | Recompute partition | Update peer-aware rank state when cheaper than recomputation | Both must handle the case where one inserted row shifts every subsequent row's output — the delta is not proportional to the input change, and the release must document where the crossover to partition recomputation lies rather than pretending it does not exist. ### LT-7d: Offset and boundary candidates | Function | Current strategy | Admission target | |----------|------------------|------------------| | `LAG(col, N)` / `LEAD(col, N)` | Recompute partition | Maintain enough ordered state for bounded offsets | | `FIRST_VALUE()` / `LAST_VALUE()` | Recompute partition | Maintain exact frame-boundary state | | `NTH_VALUE()` | Recompute partition | Maintain indexed ordered state for the admitted frame | These are semantic and performance goals, not commitments to a ring buffer or another specific data structure. The implementation must handle insertion and deletion at arbitrary positions before an offset strategy is admitted. ### LT-7e: Aggregate-over-window candidates | Function | Current strategy | Admission target | |----------|------------------|------------------| | `SUM() OVER (...)` | Recompute partition | Maintain bounded algebraic state for admitted frames | | `COUNT() OVER (...)` | Recompute partition | Maintain bounded algebraic state for admitted frames | Non-invertible aggregates over windows (`MIN`/`MAX` with deletions) stay on the recomputation path unless a proof is available, consistent with the fail-closed rule established in [v0.83.0](v0.83.0.md). ### LT-7f: Fallback, reason codes and the support matrix Frames that are not covered — `ROWS BETWEEN` / `RANGE BETWEEN` variants outside the algorithms above, `EXCLUDE` clauses, `GROUPS` mode — fall back to partition recomputation with a reason code visible in refresh history and `explain()`, and are listed explicitly in `docs/DVM_SUPPORT_MATRIX.md`. A window function whose incremental algorithm is unavailable must never silently produce a different answer from PostgreSQL. ## Exit criteria - [ ] Auxiliary state model shipped, bounded, crash-tested and covered by the backup/restore suite - [ ] Every candidate in LT-7c/d/e either passes the admission gate or has an explicit reason for staying on recomputation - [ ] Property tests compare DIFFERENTIAL with FULL after every INSERT, UPDATE and DELETE for each admitted window shape, including peers, NULL ordering, collations, and frame boundaries - [ ] Measured speed-up published per function family, including the crossover point and output amplification where recomputation is still faster - [ ] Unsupported frames fall back with a reason code and appear in `docs/DVM_SUPPORT_MATRIX.md` - [ ] Existing window stream tables upgrade without user action, or are flagged for reinitialisation with an actionable message