Skip to content

feat(planning): price the derived window layouts so they can be ranked - #697

Closed
zzylol wants to merge 1 commit into
fix/window-candidate-shapefrom
feat/window-layout-costing
Closed

feat(planning): price the derived window layouts so they can be ranked#697
zzylol wants to merge 1 commit into
fix/window-candidate-shapefrom
feat/window-layout-costing

Conversation

@zzylol

@zzylol zzylol commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Stacked on #696. Base is fix/window-candidate-shape, so this diff is only
the costing. Merge #696 first; this retargets to main automatically.

Why

A sliding window has two legal physical layouts and they trade opposite
resources:

  • Pane { P } takes each sample into exactly one pane, and composes W / P of
    them per read.
  • FullWindow takes each sample into all ceil(W / S) overlapping windows, and
    is read whole.

Which is cheaper depends entirely on how hard the source is pushing. Nothing was
comparing them. #696 emits only the pane form, and a lone candidate is selected
by complete_summary_candidate_estimate's min_by over a one-element list —
the cost value cannot change the outcome, so the deployment never gets the
choice.

What

For a sliding derived shape, both layouts are emitted and each is priced, so the
existing ranking has something real to compare. Tumbling shapes are untouched:
the validator's framework/layout table pairs Tumbling only with Pane, so
there is no alternative to rank.

Plan-compile time only. Snapshots that supply their own window_candidates are
unaffected.

How

derived_window_cost composes the snapshot's own lifecycle unit costs with each
layout's structural counts, over horizon_seconds, for window W and slide S:

term Pane { P } FullWindow
states sealed horizon / P horizon / S
update fanout per sample 1 ceil(W / S)
finalizations per read ceil(W / P) 1
retained states retained_state_count(…) retained_state_count(…)
cpu_cost      = build·states + maintenance_per_update·(rate·horizon·fanout)
weighted_cost = cpu_cost + read·reads·finalizations
                         + retention_per_second·horizon·retained
                         + retirement·states

Nothing here is invented. Every unit cost is supplied evidence
(implementation.lifecycle_costs); every multiplier is a structural count that
follows from the layout's own definition — the same fanout worker.rs's
stores_full_windows branch actually performs. Retention reuses
retained_state_count, the function that fills num_aggregates_to_retain, so
the quote and the plan cannot disagree. Concurrent in-flight full windows are
not charged twice: the fanout term already prices that write amplification.

The byte fields are passed through from the snapshot unchanged. State size needs
sketch parameters that do not exist at this point, and guessing them would be
exactly the fabrication this otherwise avoids. Only cpu_cost and
weighted_cost are derived, and only those two are read — by
validate_window_implementations and by the ranking min_by. model_version
is suffixed +derived-window-layout-v1 so a derived quote is never mistaken for
a producer's measurement.

Before this PR

One layout per window, chosen structurally. A deployment could only get the
other by hand-supplying both candidates with its own priced evidence, which
nothing in the backend produces.

After this PR

Both layouts are offered for a sliding shape and the cheaper one wins on the
deployment's own numbers. Against the planning snapshot's evidence
(build 10.0, maintenance_per_update 0.001, read 0.1,
retention_per_second 0.001, retirement 1.0, horizon 300s, W=300, S=30):

ingestion rate Pane{30} FullWindow selected
0/s 143.3 113.3 FullWindow
1/s 143.6 116.3 FullWindow
100/s (the snapshot's declared rate) 173.3 413.3 Pane{30}

The crossover is where the tenfold update fanout overtakes the ten merges per
read. At the snapshot's own declared rate panes win, so
five_minute_lookback_evaluated_every_thirty_seconds_slides_by_thirty still
plans Pane{30} retaining 11 — this PR changes no selection for that workload,
it makes the selection a decision instead of a default.

Evidence

Execution example — the table above is produced by
derived_window_layout_prices_write_against_read_amplification, which asserts
the two directions rather than the magnitudes:

rate=0    id-pane-30s    Pane{30}    cpu=100    weighted=143.3
          id-full-window FullWindow  cpu=100    weighted=113.3   <- selected
rate=100  id-pane-30s    Pane{30}    cpu=130    weighted=173.3   <- selected
          id-full-window FullWindow  cpu=400    weighted=413.3

Performance measurement: not applicable — no runtime path changes. The numbers
above are the cost model's own output on supplied evidence, not measurements of
either layout.

Screenshot: not applicable.

Verification

  • Unit tests:
    • derived_window_layout_prices_write_against_read_amplification — with no
      arriving data the full window is cheaper; at the snapshot's declared rate
      panes are. Pins the crossover direction, deliberately not the magnitudes.
    • tumbling_shapes_have_no_layout_alternative_to_rank — a tumbling shape
      still yields one candidate keeping the snapshot's identity.
    • derived_window_candidate_follows_the_evaluation_cadence — extended: a
      sliding shape now offers both layouts, in a stable order.
    • derived_window_candidate_shapes_are_accepted_by_validation — already
      walked every emitted shape through validate_window_implementations; it now
      covers the FullWindow candidates too, so a mispriced or malformed
      alternative is a compile error rather than a plan.
    • Full suite: cargo test -p control_plane — 809 passed, 0 failed (770 lib =
      768 from fix(planning): derive the fallback window implementations from the query #696 + 2 new, plus 31 api + 8 integration). No existing
      assertion changed.
  • End-to-end tests: not run. cargo test -p data_plane --lib does not compile
    in this checkout — asap_sketchlib is missing accepts_standard_updates
    (univmon_accumulator.rs:48) and quantile_interpolated
    (summary_executor.rs:1053). Pre-existing and unrelated (this PR touches one
    control-plane file), but it means no data-plane evidence is offered here.
  • Other checks: cargo fmt -p control_plane -- --check clean; cargo clippy -p control_plane --all-targets warning count unchanged against the base branch
    (66 → 66).

Architectural decisions

Compose supplied unit costs; never synthesize a magnitude. Rejected
alternative: give both candidates the snapshot's single quote verbatim. Then
min_by tie-breaks alphabetically on implementation_id and the "choice" is
arbitrary. Rejected alternative: attach plausible constants per layout. That is
fabricating measurement, which the weighted_cost contract exists to prevent.
Composition keeps every number traceable to either supplied evidence or the
layout's definition.

Reuse retained_state_count for the retention term rather than modelling
retention independently. It is what actually drives num_aggregates_to_retain,
so a second formula could price a plan the backend will not build.

Leave the byte fields alone. They are for auditability, and a derived
guess would make the audit worse, not better. model_version carries the
provenance instead.

Limitations and follow-up

  • The formula is a model, not a measurement. It ranks correctly in the
    directions the tests pin, but its absolute magnitudes are only as good as
    lifecycle_costs. A deployment with real per-layout measurements should
    supply window_candidates and bypass this entirely — that path is unchanged
    and still takes precedence.
  • HierarchicalRollup is priced but never derived. derived_window_cost
    handles it (a base pane behaves like Pane), so a snapshot supplying one is
    costed consistently, but nothing emits one: it needs the
    Extension("backend.exact-hierarchical-rollup.v1") framework.
  • implementation_id now encodes the layout for sliding shapes
    (…-pane-30s, …-full-window), because two candidates for one window must
    have distinct ids. Single-candidate tumbling shapes keep the snapshot's label
    unchanged.
  • Selecting FullWindow can reach the derived-maintenance gate.
    maintenance_runtime.rs:1108/:1269 still reject a sliding cohort for
    materializations with a derived_input. Live-ingest is unaffected. Note
    validated_source_window_cohort requires FullWindow for a sliding derived
    cohort, so this PR makes that layout reachable — lifting the runtime gate
    remains its own change, with its own validation, for the reasons recorded on
    fix(planning): derive the fallback window implementations from the query #696.

Human review — do not complete with an agent

  • The MVP boundary is correct.
  • New conceptual layers or public interfaces are necessary.
  • The before/after description matches the intended product behavior.
  • Human reviewer:
  • Decision and rationale:

🤖 Generated with Claude Code

A sliding window has two legal physical layouts, and they trade opposite
resources. `Pane { P }` takes each sample once and composes `W / P`
states per read; `FullWindow` takes each sample into all `ceil(W / S)`
overlapping windows and is read whole. Which is cheaper depends on how
hard the source is pushing, and nothing was comparing them: the
derivation emitted only the pane form, and a lone candidate is selected
by a `min_by` over one element.

Emit both for a sliding shape and price each from the snapshot's own
lifecycle unit costs.

`ImplementationCostEvidence` is normally measured evidence, so becoming
its producer here is only sound if nothing is invented. Every unit cost
is supplied (`implementation.lifecycle_costs`), and every multiplier is
a structural count that follows from the layout's definition: states
sealed per horizon, update fanout, finalizations per read, and
`retained_state_count` -- the same function that fills
`num_aggregates_to_retain`, so quote and plan cannot disagree.
Concurrent in-flight full windows are not charged twice; the fanout term
already prices that write amplification.

The byte fields stay exactly as the snapshot supplied them. State size
needs sketch parameters that do not exist yet at this point, and
guessing them would be the fabrication this avoids. Only `cpu_cost` and
`weighted_cost` are derived, and only those two are read -- by
`validate_window_implementations` and by the ranking `min_by`.
`model_version` records that the quote is derived.

Against the planning snapshot's own evidence the crossover lands where
it should: idle, the full window wins because its fanout is free and the
pane's ten merges per read are not; at the declared 100 updates/s the
tenfold fanout dominates and panes win. Tumbling shapes are unchanged --
the validator pairs them only with `Pane`, so there is no alternative to
rank and their identity keeps the snapshot's label.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant