Skip to content

feat(planning): register ASAPPlanner's grouping strategy in selection - #692

Merged
zzylol merged 1 commit into
mainfrom
feat/wire-all-replacement-strategies
Sep 12, 2026
Merged

zzylol merged 1 commit into
mainfrom
feat/wire-all-replacement-strategies

Conversation

@zzylol

@zzylol zzylol commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Why

ASAPPlanner's value at this boundary is that it enumerates the complete legal
candidate space and leaves the commitment to the deployment. Selection named
three of the five context-free replacement strategies and said nothing about the
rest, so it was not recorded anywhere which parts of that space the backend
actually consumes, or why. One axis — grouping — was genuinely missing: the data
plane can execute a shared multi-subpopulation grid (accumulator_spec.rs:261
maps GroupingStrategy::SharedMultiSubpopulation to AggregationType::HydraKLL)
but no backend plan could ever propose one.

What

Planning-time candidate enumeration only. No change to serving, to the physical
wire contract, or to any published plan.

  • Register HydraGroupingStrategy.
  • Extract the list into replacement_strategies and state, in one place, why
    each remaining strategy is absent.

Registered: SketchAlgorithmStrategy, HydraGroupingStrategy,
ExactCompositionStrategy, SemanticEquivalentRewriteStrategy.

How

RollupStrategy, AccuracyReconciliationStrategy and TopKLimitReuseStrategy
are workload-dependent and were already active: search_cse_workload_with
(replacement.rs:5186-5195) builds all three from the post-CSE sibling set and
asks them on every discovered target. Only that function owns the sibling set,
so registering them here would hand each an empty one and report nothing.

SharedSubtreeStrategy is withheld, and the doc comment now says why in terms a
reviewer can check. Both of its arms are Replacement::Rewrites of the target
itself; rank_group ranks that pair (its "Shape 1") ahead of every
Replacement::Summary in the same group and returns before the sketch-family
ranking runs. The winning rewrite materializes as KeepPreAsap. Registering it
therefore downgrades every shared aggregate from its selected sketch to raw
execution. This is a Planner limitation, not a cost policy the deployment can
express: no CostModel hook reorders past Shape 1, and picking the other arm
selects a rewrite too. The fix belongs upstream — carry the selected summary
through the share rewrite — and until then canonical CSE inside
search_workload_with_targets already shares these subtrees structurally and
correctly.

Before this PR

Three strategies registered, with a comment that explained only why CSE's
recompute arm was unwanted. A grouped aggregate could only ever be planned as one
independent sketch instance per by key. Nothing recorded that three further
strategies were already running inside the search, so the registered list read as
the complete picture when it was not.

After this PR

The grouping axis is enumerated, so memo groups contain the Hydra alternative
alongside the independent-instance ones and selection_trace reports each with
its strategy, provenance, rationale and selected/unselected status — once
shared-grid evidence is supplied; see Limitations.

Selection results are unchanged for every workload today: Hydra produces no
candidate without that evidence, and all 758 pre-existing control_plane lib tests
pass unmodified.

replacement_strategies' doc comment is now the single place that answers "which
strategies does this deployment use, and why not the others" — including the two
categories that are absent for completely different reasons (already-registered
upstream vs. actively unsafe at this pin).

Evidence

Execution example — Hydra candidate generation for a grouped Count, from
hydra_candidates_wait_for_shared_grid_evidence:

accuracy = EpsilonDelta { epsilon: 0.01, delta: 0.01 }
target   = Aggregate { reduction: Reduce(by [job]), measures: [Count] }

NoAccuracyEvidence                  -> 0 candidates   (guarantee not composable)
provider reporting
  hydra_shared_grid_collision_bound
  hydra_shared_grid_failure_probability
                                    -> 2 candidates   (HydraCms over Cms,
                                                       HydraCountSketch over CountSketch)

Execution example — the SharedSubtreeStrategy hazard, measured by registering
it and running shared_aggregates_keep_their_summary (grouped Count over a
schema with a unique key, so canonical CSE interns the subtree):

with SharedSubtreeStrategy registered:
  1 query   -> [summary]                 CMS selected
  2 queries -> [raw, raw]                neither root is a summary any more
                                         assertion failed: left [false, false]
                                                           right [true, true]
without it (this PR):
  1 query   -> [summary]
  2 queries -> [summary, summary]

Performance measurement: not applicable — this changes which candidates are
enumerated at plan-compile time, not any runtime path.

Screenshot: not applicable — no visual output.

Verification

  • Unit tests:
    • registered_strategies_are_exactly_the_supported_set — the registered list
      is exactly the four supported strategies, in discovery order (which is also
      rank_group's tie-break order); guards against either exclusion being
      undone by mistake.
    • hydra_candidates_wait_for_shared_grid_evidence — Hydra offers nothing under
      NoAccuracyEvidence and exactly two candidates under a provider that reports
      the shared-grid bounds; pins both the wiring and the missing input.
    • shared_aggregates_keep_their_summary — one grouped count selects a summary
      and two identical ones must each still get one; fails if
      SharedSubtreeStrategy is registered at this Planner pin.
    • Full suite: cargo test -p control_plane — 800 passed, 0 failed (761 lib =
      758 pre-existing + 3 new, plus 31 api + 8 integration); no existing test
      modified.
  • 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). Confirmed pre-existing by reverting the changed
    file to origin/main and reproducing the identical two errors, so it is
    unrelated to this change — but it does mean no data-plane evidence is offered
    here.
  • Other checks: cargo fmt -p control_plane -- --check clean; cargo clippy -p control_plane --all-targets reports no new warning in the changed file.

Architectural decisions

Withhold SharedSubtreeStrategy rather than try to out-rank its rewrite.
Rejected alternative: register it and pin ControlPlaneCostModel::cse_share_decision
to Share, so only the share arm can win. That was the first shape of this PR
and it is wrong: the share arm is itself a logical rewrite, so it still
materializes as KeepPreAsap and still loses the sketch. rank_group returns
from Shape 1 before any cost hook could reorder a Summary ahead of it, so no
cost-model policy can express the intended outcome. Withholding the strategy and
pinning the behavior with a regression test is the honest option at this pin.

Do not invent a shared-grid collision bound to make Hydra selectable.
Rejected alternative: default hydra_shared_grid_collision_bound to 0.0 so
candidates survive. That would let a shared grid win on an accuracy claim nothing
measured, contradicting the convention this crate states for every other cost
input ("unknown values stay unknown, preventing optimistic zeroes"). Registration
and evidence stay separate, individually reviewable steps.

Leave estimated_subpopulation_count unimplemented. It changes which
candidate wins, not whether it is enumerated, and needs its own evidence story.

Limitations and follow-up

  • Hydra is registered but cannot yet be selected. QueryEvidence::propagation_stats
    (compiler.rs:447) fills only the TopK fields, so
    hydra_shared_grid_collision_bound / hydra_shared_grid_failure_probability
    are None and build_candidate's satisfies check drops every Hydra
    candidate. That one function is the whole remaining gap; the test above fails
    loudly if this stops being true in either direction.
  • Hydra covers only CMS-family intents. hydra_kind_for maps Cms and
    CountSketch only, so KLL/DDSketch quantiles have no Hydra variant at planner
    pin 3be523fa. AggIntent::TopK binds CmsWithHeap/CountSketchWithHeap,
    also unmapped.
  • Ranking Hydra against independent grouping needs a subpopulation estimate.
    ControlPlaneCostModel does not override estimated_subpopulation_count, so
    rank_group's grouping comparison is skipped and candidates keep discovery
    order (independent first). ERP's observed_populations is a plausible source;
    deliberately deferred.
  • SharedSubtreeStrategy needs an upstream fix before it can be registered
    see How. Filed as follow-up work against Planner, not worked around here.
  • Hydra candidate construction is performed and discarded on every grouped
    aggregate.
    HydraGroupingStrategy::matches is !hydra_candidates().is_empty(),
    and hydra_candidates runs a full construct_summary_with bind per mapped
    sketch kind. Because the accuracy gate above always rejects today, that work is
    thrown away for every grouped Count-shaped target compiled; once evidence
    lands, a matching target pays it twice (matches, then propose). Plan-compile
    time only and bounded by target count. Not measured — flagged rather than
    quantified.
  • Nothing pins the "already registered upstream" claim. That
    search_cse_workload_with asks RollupStrategy,
    AccuracyReconciliationStrategy and TopKLimitReuseStrategy
    (replacement.rs:5247, :5256, :5268) was verified by reading the pinned
    source, not by a test. A future Planner bump could drop it and silently cost
    the backend three strategies. A trace-level assertion would close this.
  • hydra_candidates_wait_for_shared_grid_evidence builds the strategy
    directly
    , not through replacement_strategies, so it does not prove the
    registered instance carries the same models. Combined with the registration
    test the coverage holds, but the two can drift.
  • No data-plane verification, for the pre-existing build failure described under
    Verification.

Review

Self-review only — the author performed it; not independent, per
.agents/skills/asap-code-review. It corrected two things in this PR: the doc
comment and test name claimed to register "every context-free strategy" while
deliberately excluding one, and the verification counts conflated 758
pre-existing lib tests with the 761 post-change total. The three items it could
not close are recorded under Limitations.

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

@zzylol zzylol changed the title feat(planning): register every ASAPPlanner replacement strategy feat(planning): register every context-free ASAPPlanner replacement strategy Sep 12, 2026
@zzylol
zzylol force-pushed the feat/wire-all-replacement-strategies branch from 112a5d7 to 81caed9 Compare September 12, 2026 14:31
@zzylol zzylol changed the title feat(planning): register every context-free ASAPPlanner replacement strategy feat(planning): register ASAPPlanner's grouping strategy in selection Sep 12, 2026
Selection registered three of ASAPPlanner's context-free replacement
strategies and named none of the rest, so it was not obvious which parts
of the legal candidate space the backend actually consumes.

Register HydraGroupingStrategy, the grouping axis: a shared
multi-subpopulation grid as an alternative to one sketch instance per
`by` key. The data plane can already execute it —
`accumulator_spec.rs:261` maps it to `AggregationType::HydraKLL` — but no
backend plan could ever propose it.

Hydra candidates additionally need a shared-grid collision bound to
compose a guarantee. `QueryEvidence` (compiler.rs:447) reports none
today, so the strategy correctly offers nothing rather than an unbounded
guarantee; the regression test pins both halves, so supplying that
evidence is all that stands between registration and selection.

Extract the list into `replacement_strategies` and record, in one place,
why the remaining strategies are absent:

- RollupStrategy, AccuracyReconciliationStrategy and TopKLimitReuseStrategy
  were already active and must stay out. `search_cse_workload_with`
  builds all three from the post-CSE sibling set and asks them on every
  target; only it owns that set, so registering them here would hand each
  an empty one and report nothing.

- SharedSubtreeStrategy stays out for a Planner limitation, not a cost
  policy. Both of its arms are `Replacement::Rewrite`s of the target
  itself, and `rank_group` ranks that pair ahead of every
  `Replacement::Summary` in the group and returns before the
  sketch-family ranking runs. The winning rewrite materializes as
  `KeepPreAsap`, so registering it downgrades every shared aggregate from
  its selected sketch to raw execution. `shared_aggregates_keep_their_summary`
  pins that: one grouped count selects a summary, and two identical ones
  must still each get a summary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zzylol
zzylol force-pushed the feat/wire-all-replacement-strategies branch from 81caed9 to c59ab0b Compare September 12, 2026 14:36
@zzylol
zzylol merged commit 96504c7 into main Sep 12, 2026
1 check passed
@zzylol
zzylol deleted the feat/wire-all-replacement-strategies branch September 12, 2026 14:47
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