Repository navigation
Tracking: make rqe-optimizer the solver for ASAPQuery's planner #143
Description
Goal
Make rqe-optimizer's MILP the solver behind ASAPQuery's asap-planner-rs. ASAPQuery calls it as a library (git dependency, pinned rev), converts its query workload into Raqes, and converts the returned plan into StreamingConfig / InferenceConfig. ASAPQuery side: see the linked ASAPQuery tracker.
Out of scope for this round: Subtract query method (follow-up), spatial-filter sharing (ASAPQuery errors on spatial filters for now), memory bounds.
TODO
- S1. Raqe input model
- Rename
Rqe→Raqewith fields:id,capability,metric,grouping_labels,spatial_filter,lookback_ms,interval_ms,accuracy_metric,accuracy_sla,accuracy_direction,latency_sla. - Latency limit moves from
MilpBoundsintoRaqe. - Units: milliseconds instead of whole seconds.
- Stream identity for eligibility and sharing = (metric, spatial filter, grouping labels).
- Rename
- S2. Workload facts input (replaces
LabelSetInfo) (sketch-bench#145)- Per metric: full label list and
arrival_rate_per_sec. - Cardinality per (metric, label set); input/output cardinality of a Raqe come from this.
- Per metric: full label list and
- S3. Objective cleanup (sketch-bench#145)
- Keep only
minimize_cost(EC2 machine family); removeminimize_tco. - Remove the peak query-memory bound.
- Keep only
- S4a. Capabilities and families (sketch-bench#144)
- Capabilities: SumOrCount, MinOrMax, RateOrIncrease, Quantile (+ HydraKLL), Cardinality, TopK. Freq removed.
- S4b. Family properties and key tracker (includes the cost-model part of sketch-bench#142)
- Per-family property table: mergeable, subtractable, subpopulation-aware, needs key tracker.
- Non-mergeable families eligible only when
L == x. - Subpopulation-aware families: one instance, not × cardinality, in CPU and memory.
- Key tracker: deployments of families that need one also pay a DeltaSet's ingest CPU and retained memory (∝ output cardinality); the plan flags it.
- S5. Cost rows
- Exact families' accuracy: already in place. The
aggregatecomparator (rqe-optimizer: exact sum/min-max/increase accumulators, CountSketch+heap and UnivMon top-k #144) measuresrelative_error/relative_error_meanagainst per-group truth, so exact rows report ≈0. No work. - HydraKLL: benchmark exists (
hydra-klllib + polars, rows inaqpbm-cli/src/rows/hydra.rs, comparatorsubpop-rank-errorreportingmean_rank_err/max_rank_err, the same names as KLL/DD). To do: add ahydra-kllentry (or apoint_hydrahelper) toscripts/export_rqe_optimizer_costs.shwith grouped datagen (configs/datagen/hydra_columns.yaml), comparatorsubpop-rank-error, and a small param grid. If rqe-optimizer: support shared multi-subpopulation sketches (HydraKLL) in the cost model #142 needs accuracy at several cardinalities (fixed-size Hydra degrades as groups grow), measure those points here. - DeltaSet: no code exists yet. To do:
- wrapper mirroring ASAPQuery's
DeltaSetAggregator(key set per window, added/removed keys), registered like theexact-*variants; aqpbm-clirow for insert / merge / query costs;- memory normalized per key, the same way rqe-optimizer: exact sum/min-max/increase accumulators, CountSketch+heap and UnivMon top-k #144 normalizes exact types per group in
aqpbm-core/src/atomic_costs.rs; - a trivial exact comparator (keys found vs true keys, ≈0 error), since
reduce_onerequiresquery_accuracy.
- wrapper mirroring ASAPQuery's
- Run the export (
scripts/export_rqe_optimizer_costs.sh; not yet run since rqe-optimizer: exact sum/min-max/increase accumulators, CountSketch+heap and UnivMon top-k #144), checkreduce_all's skipped rows, and publish the JSON where ASAPQuery reads it.
- Exact families' accuracy: already in place. The
- S6. Candidate generation inputs
scrape_interval_ms: candidatexandymust be multiples.- Allowed families: only listed variants become candidates.
- S7. Retained memory (sketch-bench#145)
- Closed windows
(L − x)/y + 1plus openx/y(today:(x + L)/y).
- Closed windows
- S8. Plan output (sketch-bench#168)
- Per deployment: variant,
sketch_config, metric,grouping_labels,x,y, key tracker, retained instance count (max over the Raqes it serves). - Per Raqe: deployment index, merged instance count
n = L/x. - Objectives including $/h.
- Per deployment: variant,
Follow-ups
- S9 (follow-up). Subtract query method (needs
subtract_cpu_secs;asap_sketchlibhas no subtract yet).
Order and dependencies (both repos)
Labels: S1–S8 = sketch-bench #143 items in order (S1 Raqe model, S2 facts, S3 objective cleanup, S4a capabilities, S4b properties + key tracker, S5 cost rows, S6 candidate inputs, S7 retained memory, S8 plan output). A1–A6 = ASAPQuery ProjectASAP/ASAPQuery#790 items (A1 dependency/build, A2a avg rewrite, A2b inputs → Raqe, A3 plan → configs, A4 disable old optimizer, A5 wiring, A6 housekeeping).
sketch-bench ASAPQuery
S3 objective cleanup ──┐ A1 dependency + HiGHS build (vs current main)
│ A2a avg → sum/count rewrite
S1+S2 Raqe + facts ────┼─► S6 scrape / allowed families ─┐
S4a capabilities ──────┤ ├─► S8 plan output ─┐
S5 cost rows ──────────┴─► S4b properties + key tracker ─┘ │
+ S7 retained memory ▼
A2b inputs → Raqe ◄── S1, S2, S4a, S6
A3 plan → configs ◄── S8
A4+A5 disable old + wire CLI ◄── A2b, A3
A5' asap-planner --planner milp
A6 housekeeping (any time)
| Phase | Work | Depends on |
|---|---|---|
| 0 (now, parallel) | S3; S4a (commit first: S1/S4b conflict with it); S5; A1 (via [patch] path dep); A2a |
— |
| 1 | S1+S2 (one PR), then S6 | S3, S4a |
| 2 | S4b+S7 (one PR: same per-deployment memory/CPU formulas) | S4a, S5, S1 |
| 3 | S8 plan output → pin sketch-bench rev in ASAPQuery | S4b, S7, S6 |
| 4 | A2b and A3 in parallel; then A4+A5 (one PR, keeps CLI runnable); then A5′ --planner milp |
S1, S2, S4a, S6 / S8 |
| any | A6 housekeeping | — |
Critical path: S1+S2 → S4b+S7 → S8 → A3 → A4+A5. Pin the sketch-bench rev only after S8; until then ASAPQuery uses a [patch] path dependency.
Sessions (for agents)
If you are an agent told "you are session X": find your row, check that the PRs for everything in Needs are merged (gh pr list --state merged, and the ticked boxes above), read the merged PRs' descriptions for interface changes, then do Items. Don't edit files belonging to other sessions' items.
| Session | Phase | Start in | Items | Needs |
|---|---|---|---|---|
| 0a | 0 | sketch-bench | finish + merge sketch-bench#144 (S4a), then S3 | — |
| 0b | 0 | sketch-bench | S5 remainder: run the export, DeltaSet benchmark, HydraKLL benchmark (benchmark part of sketch-bench#142) | — |
| 0c | 0 | ASAPQuery | A1, A2a, A6 | — |
| 1a | 1 | parent dir with both repos | S1+S2, then S6 | 0a |
| 2a | 2 | sketch-bench | S4b+S7 (includes the cost-model part of sketch-bench#142) | 1a, 0b |
| 2b | 2 | ASAPQuery, [patch] path dep on local sketch-bench |
A2b | 1a, 0c |
| 3a | 3 | parent dir with both repos | S8 + A3, then pin the sketch-bench rev in ASAPQuery | 2a, 2b |
| 4a | 4 | ASAPQuery | A4+A5, then A5′ (--planner legacy|milp) |
3a |
Done means: tests and clippy pass; one PR per item group; Conventional Commit title; tick the item here; describe any interface change later sessions depend on in the PR description. Use a git worktree per session. Follow ASAPQuery AGENTS.md. Never git commit --amend after a failed pre-commit hook; re-run the commit.
Decisions (don't revisit)
- ASAPQuery calls sketch-bench's
rqe-optimizeras a library: git dependency pinned to a rev (pin after S8);[patch]path dependency during development. - Objective:
minimize_cost(EC2 machine family) only;minimize_tcoand the peak-memory bound are removed. - The Freq capability was removed on purpose. CMS/CountSketch appear only as top-k heaps.
- Subtract is deferred: query methods are Direct (
L == x) and Merge only. - Spatial filters: ASAPQuery errors on them for now; sharing across filters is later.
- Arrival rate is an explicit per-metric input (not derived from cardinality). Units are milliseconds.
xandymust be multiples of the scrape interval (sketch-bench input).- Families the engine can't deploy are excluded via an allowed-families input;
--allow-undeployable-familiesprints the plan and $/h without writing YAML. - Exact families report their accuracy metrics as 0.
- Accuracy: ASAPQuery maps
accuracy_slato one (accuracy_metric,accuracy_direction) per capability; families within a capability share metric names. - Every family has properties: mergeable, subtractable, subpopulation-aware, needs key tracker. Non-mergeable → only
L == x. HydraKLL needs a DeltaSet key tracker, costed in the MILP. - Merged instance count
n = L/xis per Raqe (query CPU, Direct/Merge, read counts). Retained instance count is per deployment: openx/yplus closed(L−x)/y + 1for the longestLit serves (memory cost, store retention). - ASAPQuery's old optimizer modules (
candidate_gen,cost_model,sketch_properties,constants,greedy) are commented out, not deleted, for now.
🤖 Generated with Claude Code
Activity
milindsrivastava1997 commentedon Oct 5, 2026
ASAPQuery side: ProjectASAP/ASAPQuery#790
milindsrivastava1997 commentedon Oct 6, 2026
Decision: the per-phase cost model goes into #145, along with the rest of S3
#145 removed
minimize_tco. Only the cost objective is left, now namedObjective::AUCCost. It prices mean CPU-sec/sec (the area under the load curve), so it sizes for average load, not bursts. #145 will also take on:MilpBounds.max_peak_query_memory_bytes).retained_memorycombines open and closed windows, and query memory (output size) isn't modeled at all.Decisions so far (deployment groups
G, sketch keysK,n_G = card(G)):λ · (x/y) · c_insn_G · m · x/yn_G · (L/x − 1) · c_mrg / Tn_G · m(all groups merged at once)n_G · c_qry / Tn_Gvalues,n_G · kfor TopK (new)m,c_ins,c_mrg) should grow with the number of keys per instance,card(G ∪ K) / n_G, rather than stay constants measured at the benchmark's key count.n_G · m: every group's merged accumulator is held at the same time.G. A finer deployment that gets regrouped at query time is out of scope.TopK note: a
G = {service}deployment keyed onK = {endpoint}(one CMS+heap per service) answerstopk by (service) (sum_over_time(...))only ifG ∪ Kcovers all of the metric's labels. SoKis "the metric's labels minusG", which follows from S1 (grouping_labels) plus S2 (the full label list).We're settling the exact scope in a grilling session. #145's description will list which S-items it covers once that's done.
milindsrivastava1997 commentedon Oct 6, 2026
Scope of #145 is settled, and it changes two Decisions here. The full Q&A is in #145's description.
#145 now covers S3 (including removing the peak-memory bound), S7, S2 plus
metric/grouping_labelsfrom S1, and half of S6 (xandymust be multiples of the scrape interval). The rest of S1, S4b, S5, the rest of S6 and S8 are unchanged.Changes to "Decisions (don't revisit)":
λ = cardinality[metric.labels] / scrape_interval_secs, fromMetricFacts { labels, scrape_interval_secs, cardinality }. There's no override, and validation requires a cardinality entry for the metric's full label set. Previously λ and C were independent inputs, and nothing kept them consistent.minimize_coston an EC2 family. It isObjective::AUCCost { w_cpu, w_mem }, which minimizesw_cpu · CPU + w_mem · memory_GiB, default(1, 0). Memory sums the ingest, storage, merge and query terms.MachineFamilyand the EC2 pricing snapshot are removed.Note for S4b and #142: the export already divides the exact accumulators' memory and merge cost by
groups_per_instance, socard(G) × mprices them correctly today. The "subpopulation-aware" property is only needed once a Shared + Fixed family such as HydraKLL arrives.rqe-optimizer: per-phase cost model, workload facts, weighted AUCCost
milindsrivastava1997 commentedon Oct 6, 2026
#145 covers S2, S3 and S7 completely (ticked above), and S1 and S6 partially.
Complete:
MetricFacts { labels, scrape_interval_secs, cardinality }replacesLabelSetInfo. A RAQE's input cardinality iscard(G); its output iscard(G)values, orcard(G) × 32for top-k. The arrival rate is derived ascard(labels) / scrape_interval, not given; see the earlier comment.minimize_tcoand the peak query-memory bound are removed. The objective isObjective::AUCCost { w_cpu, w_mem }, notminimize_coston an EC2 family; see the earlier comment.(L − x)/y + 1as the storage phase, and open windowsx/yas ingest memory.Partial:
metricandgrouping_labelson RAQEs and deployments. Left: theRqe→Raqerename (only the design doc says RAQE so far), millisecond units,spatial_filter, and moving the latency SLA fromMilpBoundsonto the RAQE.xandyare multiples of the metric's scrape interval (per metric, in seconds). Left: the allowed-families input.The cost table also needs work to fit the new model; that's tracked in #147.
rqe-optimizer: per-phase cost model, workload facts, weighted AUCCost…
feat(bench): exact-delta-set row and HydraKLL in the rqe-optimizer co…
milindsrivastava1997 commentedon Oct 6, 2026
S5 follow-up from S4b: HydraKLL accuracy at more group counts
S4b gates subpopulation-aware families on group count: hydra-kll is eligible only when
card(G) ≤ query_accuracy["subpopulations"]of its row. Today that's the single point the export measures, about 200 groups onhydra_kll_disjoint_labels.yaml. So hydra-kll is never planned for larger groupings, even where its accuracy might still meet the SLA.To loosen the gate, S5 should measure hydra-kll rows at several group counts (e.g. 10², 10³, 10⁴, 10⁵ via the key1 population in the spec). The optimizer then takes the smallest-memory row that covers
card(G)and meets the SLA, with no further optimizer change. Interpolating a curve between points is a later step if needed.Caveat: Hydra inserts every label subset of its key, so its error really depends on the total subpopulation count, not just
card(G). The gate ignores this for now.🤖 Generated with Claude Code
feat(rqe-optimizer): family properties, shared fixed-size sketches an…