Repository navigation
Optimizer: move sketch-bench #129 MILP into asap-planner-rs (--planner milp) #753
Description
Summary
sketch-bench PR #129 (rqe-optimizer) adds a HiGHS-backed MILP that picks sketch deployments (family, params, window, slide) for a repeating-query workload and shares deployments across compatible queries. Its output is meant to feed ASAPQuery's planner, so the solver should live in asap-planner-rs as mip_assign. That fills the Phase 3a slot in .design_docs/optimizer-v1-implementation-plan.md and closes the cross-AQE sharing gap (#650).
Goal
A production planner path, asap-planner --planner legacy|milp (default legacy), that solves the sharing-aware MILP over ASAPQuery's own candidates and cost model and emits deployable streaming_config.yaml / inference_config.yaml.
PR order
| # | PR | Issues | Notes |
|---|---|---|---|
| 1 | Retention fix | #760 | Standalone asap_types + translator bug. First: legacy already dedups configs across queries, so last-ref-wins may bite today. |
| 2 | Optimizer items + window rules | #755 + #758 | Same code (AQE struct, extract_aqes, window_candidates); T % S needs per-item T. Base for the rest. |
| 3 | Label-set facts | #756 | Independent of 2; can run in parallel. |
| 4 | SLA filters | #757 | Needs 2 (items carry SLAs). |
| 5 | avg rewrite + remove EXACT | #419 + #759 | One PR: removing EXACT alone makes every avg unservable. |
| 6 | Sketch families | #762 + #763 | Both change the candidate family set; #763 is tiny. |
| 7 | Subtract cost | #765 | After 2 (T ≠ W possible); before the MILP (objective depends on it). |
| 8 | Candidate dump + diff vs sketch-bench #129 | #767 | After 2, 4, 5, 6 so remaining diffs are real bugs. |
| 9 | MILP | #761 (+ #768 results in PR description) | |
| 10 | Planner integration | #764 | Last code PR. |
| — | Docs | #769 | After 9. |
- Parallel, any time: atomic-costs export: merge rqe-optimizer grid, add CountSketch+heap top-k, settle CMS-heap variant sketch-bench#132. Only 4 and 6 need its data; both can start with CMS/HLL/KLL.
- Not PRs yet: Optimizer: accuracy after merging n windows is unvalidated #766 (research), Optimizer MILP: explore memory as a hard bound instead of a weighted cost term #750 (exploration).
- Caveat on 5: a rewritten
avgreferences two aggregations; the engine doesn't support Avg yet (Support Avg (and other multi-statistic aggregations) in query engine #463), so in production those queries fall back to Prometheus until Support Avg (and other multi-statistic aggregations) in query engine #463 lands.
#760 ─────────────────────────────────────────┐
#755+#758 ─┬─ #757 ─┐ │
├─ #765 ├─ #767 ── #761(+#768) ── #764 ── #769
#756 ──────┤ │
#419+#759 ─┤ │
#762+#763 ─┘────────┘
sketch-bench#132 ──┘ (feeds #757/#762 data)
Related
#650, #649, #652, #750, #526, #525, #563, #533; sketch-bench #129
🤖 Generated with Claude Code
Tracking
Design decisions: first comment below (kept in sync with the issues listed here).
Cost data
- atomic-costs export: merge rqe-optimizer grid, add CountSketch+heap top-k, settle CMS-heap variant sketch-bench#132 atomic-costs export: merge rqe-optimizer grid, CountSketch+heap, CMS-heap variant
Model changes (before the MILP)
- Optimizer: assignment unit = (AQE, repeat interval, accuracy_sla, latency_sla) #755 Assignment unit = (AQE, T, accuracy_sla, latency_sla)
- Optimizer: accept externally provided label-set facts (series count, cardinality) #756 External label-set facts (series count, cardinality)
- Optimizer: enforce accuracy_sla and latency_sla in candidate eligibility #757 Enforce accuracy_sla / latency_sla in eligibility
- Optimizer: window compatibility = lookback%W, T%S, W%S per item #758 Window compatibility = lookback%W, T%S, W%S
- Optimizer: remove EXACT candidate; error on unservable items #759 Remove EXACT; error on unservable items
- planner/optimizer: decide support for multi-statistic AQEs (e.g. avg) #419 Rewrite avg → sum/count
- Add optimizer aggregation families and capability/SLA metadata #762 Add DDSketch, CountSketch(+heap), UnivMon to AggregationType
- Optimizer: HLL as the only cardinality candidate (drop Set/DeltaSet as cardinality values) #763 HLL as the only cardinality candidate
- Retention: take max across shared aggregation refs; sliding depth (n-1)*W/S+1 #760 Retention: max across shared refs; sliding depth
Verification before the MILP
- Optimizer: print candidates for a workload and diff against sketch-bench #129 #767 Print candidates for a workload and diff against sketch-bench Re-organize code in repo to be more OSS friendly #129
MILP
- Optimizer: mip_assign (good_lp + HiGHS) and asap-optimizer-cli --solver greedy|mip #761
mip_assign(good_lp + HiGHS) +asap-optimizer-cli --solver - Optimizer: compare mip_assign against sketch-bench #129 MILP (one-off script) #768 Compare
mip_assignagainst sketch-bench Re-organize code in repo to be more OSS friendly #129 (one-off) - asap-planner: --planner legacy|milp integration #764
asap-planner --planner legacy|milpintegration
Docs
- Docs: update optimizer-mip-formulation.md for the implemented MILP #769 Update optimizer-mip-formulation.md (with docs(optimizer): reconcile optimization and sketch-bench formulations #652)
Known model limitations / follow-ups
- Optimizer cost model: Subtract cost assumes T = W #765 Subtract cost assumes T = W
- Optimizer: accuracy after merging n windows is unvalidated #766 Accuracy after merging n windows unvalidated
- Optimizer MILP: explore memory as a hard bound instead of a weighted cost term #750 Memory as hard bounds instead of a cost term
- labels_compatible() is exact-match only, not superset matching #649 Label superset matching
Activity
milindsrivastava1997 commentedon Oct 3, 2026
Design decisions (revised)
Revised after follow-up scoping; each row links the issue that implements it.
--planner legacy|milp; default stayslegacy. Greedy remains anasap-optimizer-clibaseline.frequency = count / T; no collapsing intoΣ1/T/t_repeat_gcdacross differing T or SLAs.--atomic-costs,--atomic-cost-workload, and an externally provided label-set facts file:series_countper (metric, spatial filter) andcardinalityper (metric, spatial filter, grouping labels); arrival rate derived asseries_count / scrape_interval. Label schema from the workload'smetrics:hints. Replaces--dataset/--rho/SUBPOPULATION_COUNT. Missing facts → error. Cardinality scales cost per sketch class (per-group, unbounded keyedMultiple*, bounded keyed CMS/HydraKLL); trivial accumulators use an analytical per-key memory estimate.--planner milpwithsql/elasticis an argument error.windowing,sketch_parameters, non-zerorange_duration_ms/step_ms→ error.aggregate_cleanup→ honored.enable_punting,existing_*_config→ ignored.asap_types::capability_matching(exact labels; unfiltered config serves filtered queries). Label superset matching stays separate.L % W == 0,T % S == 0,W % S == 0(tumbling: S = W). Same as #129'sis_eligible. Replaces the doc's FRESHt/FRESHs bounds.enumerate_candidates, per item, unioned and deduplicated by config. Dominance pruning (from #129) later if solve time requires it.query_accuracy[metric(family)]vsaccuracy_sla, via a fixed per-family metric table (CMS →relative_error_mean, KLL →mean_rank_err, HLL →relative_error, CMS-heap →1 − recall_at_k, ...).0.0= unconstrained; missing measurement = ineligible; exact accumulators pass.latency_sla(seconds).0.0= unconstrained.avgintosum/countAQEs (divide at query time).cost_model:Σ u[g]·ingest_cost(g) + Σ z[i,g]·freq_i·query_cost(i,g). No peak-memory variable. Alternative (CPU-only + memory as hard bounds) explored separately.z[i,g],u[g];Σ_g z[i,g] = 1,z ≤ u,u ≤ Σ_i z[i,g].good_lp+ HiGHS. Addcmake(andclangif needed) to Dockerfile and CI. No cargo feature gate.--milp-time-limit-secs(default ~60): on timeout use the best incumbent and log the gap; no incumbent → error. No fallback to greedy/legacy.OptimizerSolution→IntermediateAggConfig+ query refs; reuse the legacy YAML writer (build_aggregation_entry,build_queries_yaml). Typedto_yamldeferred.num_aggregates_to_retainacross query refs (today: last one wins). Sliding Merge retains(n−1)·W/S + 1.DDSketch,CountSketch(+heap for top-k),UnivMontoAggregationType; engine returns "unsupported"; optimizer considers them only with--milp-allow-unsupported-sketches(default off).export_atomic_costs.sh(one document, one profile); add CountSketch+heap top-k; settle CMS-heap fastpath vs regularpath from engine code.mip_assignvs #129, including the query-memory definition difference. PR 3 also testsmip_cost ≤ greedy_costunder identical weights.optimizer-mip-formulation.mdto the implemented MILP, coordinated with #652; fold this file in.Delivery order
mip_assign+asap-optimizer-cli --solver greedy|mip, then Optimizer: compare mip_assign against sketch-bench #129 MILP (one-off script) #768asap-plannerintegrationKnown model limitations
References
rqe-optimizer/src/{milp,candidates,objectives}.rs,docs/rqe_sketch_deployment_v1.md.design_docs/optimizer-mip-formulation.md,.design_docs/optimizer-v1-implementation-plan.mdmilindsrivastava1997 commentedon Oct 5, 2026
ASAPQuery optimizer vs sketch-bench
rqe-optimizer: differences (as of 2026-10-05)Compared: ASAPQuery
main(after #776, #780; #781 and #786 still open) vs sketch-benchmain(after #129, #131, #135, #137).Inputs
rqe-optimizerRqe {capability, labels, lookback_secs, interval_secs, accuracy_metric, accuracy_tolerance, accuracy_direction}, built by the caller (no query parser)OptimizerItem {requirements (metric, statistics, range, grouping labels, spatial filter, topk), query_strings, query_frequency_hz = count/T, t_repeat_ms, accuracy_sla, latency_sla}, parsed from PromQLLabelSetInfo {cardinality, arrival_rate_per_sec}per label setseries_countper (metric, filter) +cardinalityper (metric, filter, labels) →ItemFacts {output_group_count, topk_by_group_count, arrival_rate_per_sec}aqpbm_core::AtomicCostEntrydirectly; sketches named by sketch-bench variant stringAtomicCostEntryin a versioned document with workload-profile selection; mapped toAggregationType+ params viasketch_bench_keyaccuracy_slaper item, metric chosen per family by the planner (#781)MilpBounds)latency_slaper item (#781)MachineFamily(EC2 vCPU, GiB, $/h)CostWeights(w_ingest_mem, w_ingest_cpu, w_query_mem, w_query_cpu)Candidates and constraints
x%y,L%x,T%yL/xinstanceskey_tracker_ingest_costMultiple*), CMS, CMS-heap, KLL, HydraKLL, HLL, Set, DeltaSetenumerate::unservableObjective
minimize_tco/ weightedw·ingest_mem + w·ingest_cpu + w·query_mem + w·query_cpu(+ key-tracker cost). #750 closed: memory stays in the objectiveminimize_costn ≥ CPU/vCPU,n ≥ retained GiB / instance GiBλ × (x/y) × insertλ × ceil(W/S) × insert(same shape)card × (query + (n−1)·merge) / Tmerge + subtract + querycard × mem; retainedcard × mem × (x+L)/y, max over RQEs a deployment servesn_concurrent × groups × mem; queryn × groups × mem(Merge) /2×(Subtract)MILP
good_lp+ HiGHSz[i,d],u[d], plus retained-GiB per deployment andinstanceswhen pricingΣz = 1,z ≤ u,u ≤ Σz; latency/memory bounds fixz = 0; family:cpu/vCPU ≤ n,Σ retained GiB / GiB ≤ n,retained·z ≤ retained_gib[d]Outputs
Mapping(deployment index per RQE) +DeploymentlistOptimizerSolution:AggregationConfigs with ids, item → aggregation (+ key aggregation) refs, query methodObjectives: CPU breakdown, peak query memory, retained memory, per-RQE latency; $/h viaMachineFamilyStreamingConfig+InferenceConfig(cleanup: read-based, #786)🤖 Generated with Claude Code
milindsrivastava1997 commentedon Oct 5, 2026
Direction changed: instead of porting the MILP into ASAPQuery, asap-planner-rs will call sketch-bench's
rqe-optimizeras a library. New plan: #790 (sketch-bench side: ProjectASAP/sketch-bench#143).milindsrivastava1997 commentedon Oct 12, 2026
SQL support for
--planner milp: #834