Repository navigation
rqe-optimizer: per-phase cost model, workload facts, weighted AUCCost objective - #145
Merged
Merged
Conversation
milindsrivastava1997
marked this pull request as ready for review
October 5, 2026 19:22
Replace minimize_tco/minimize_cost with minimize(..., Objective), where
Objective::{Tco, Cost(&MachineFamily)} owns its scaling units, extra
variables, rows and goal. The shared assignment/activation/bound rows no
longer branch on the family, and TCO no longer adds an unused instances
variable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MILP now only minimizes hourly price on a machine family. AUCCost names what it prices: mean CPU-sec/sec, the area under the load curve, so bursts are not priced. small_problem --milp now requires --machine-family. The former TCO tests run under AUCCost with the same expected mappings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
milindsrivastava1997
changed the base branch from
rqe-optimizer-capabilities
to
main
October 6, 2026 00:23
milindsrivastava1997
force-pushed
the
rqe-milp-objective
branch
from
October 6, 2026 00:23
373bc6d to
ce6008d
Compare
milindsrivastava1997
marked this pull request as draft
October 6, 2026 00:23
milindsrivastava1997
marked this pull request as ready for review
October 6, 2026 00:24
9 of 10 tasks
- Costs split into ingest, merge, query and storage, each with CPU and memory. objectives.rs holds the one copy of the formulas; score() and the MILP share it. - Storage counts closed windows as (L - x)/y + 1 for the longest lookback (#143 S7). Ingest memory is open windows only. - MetricFacts { labels, scrape_interval_secs, cardinality } replaces LabelSetInfo (#143 S2). The arrival rate is derived as card(labels) / scrape interval. validate_facts reports every bad fact. - Rqe and Deployment carry metric and grouping_labels. - Candidate windows and slides are multiples of the scrape interval (#143 S6, half). - Objective::AUCCost { w_cpu, w_mem } minimizes w_cpu * CPU + w_mem * memory GiB, default (1, 0). MachineFamily, the EC2 pricing snapshot and fetch_ec2_pricing.py are removed, as is the peak-memory bound (#143 S3). - Objectives is per phase; the Pareto vector is (CPU, memory, latencies). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per deployment or per RQE, per group, per window and per key, plus what is not shared between RQEs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Descriptive names in place of single letters, named ingest helpers in candidate pruning, and shorter comments without issue references. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
State that gcd(x, T) is the largest slide serving an RQE, why each subset of RQEs gets the gcd of theirs, and why one pass computes all of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stored windows are live objects already counted in storage, so a merge adds only the accumulator per group. A query with L == x merges nothing. Candidate pruning now compares merge memory per served RQE, since it is no longer the same instance size for every RQE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
candidate_dominates and strictly_better walked the same costs twice. A single dominance() collects (replacement, original) cost pairs once and reports strictly better, equal or not dominating. Docs: define open and closed instances, fix the candidate pseudocode indentation, and reword card(G) and merge memory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Defines family, configuration, instance and the other terms the cost model uses, links scripts/export_rqe_optimizer_costs.sh, explains what a key is and why quantile sketches are treated as fixed size, and drops the per-cost breakdown table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 of 7 tasks
… to PlanCost The module holds the per-phase cost formulas and score(); its output is a plan's cost, not an objective, and Objective already names what the MILP minimizes. In milp.rs, name the storage variables and cost closures for what they hold: deployment_storage_cost, storage_cost_for_rqe_and_deployment and merge_and_query_cost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zzylol
reviewed
Oct 6, 2026
| in which case it appears as another subset gcd. | ||
| `g_i = gcd(x, T_i)` is the largest slide that serves RAQE `i`. | ||
|
|
||
| **Windows.** An RAQE merges `S_i / x` whole windows, so `x` must divide `S_i`. |
Collaborator
There was a problem hiding this comment.
Given that you model the window sharing in the doc, can you model the sharing across queries in the doc as well, which will guide the MILP search space?
zzylol
reviewed
Oct 6, 2026
| objectives.tco_cpu_secs_per_sec, | ||
| ]; | ||
| vector.extend(&objectives.query_latency_secs); | ||
| fn pareto_vector(plan_cost: &PlanCost) -> Vec<f64> { |
Collaborator
There was a problem hiding this comment.
What does pareto in milp refer to?
Contributor
Author
There was a problem hiding this comment.
Good catch. MILP doesn't use the Pareto vector. This was added earlier for manual exploration of different plans that are the Pareto frontier of "CPU, memory, latency". Dead code right now.
This was referenced Oct 6, 2026
Merged
2 tasks done
zzylol
added a commit
that referenced
this pull request
Oct 6, 2026
Runner for ASAPQuery#777's comparison on main's planner: one metric per stream of the evaluation table, ASAP by milp::minimize, PerQuery by minimize per RQE, AutoSketch by autosketch::plan, all scored by analytical_cost_model::score (#145) at #777's weights (CPU only; Fargate $/vCPU-h and $/GB-h) and SLA grid {0.01, 0.1, 1, 10} ms and none. Each RQE's accuracy rides in the cost entries: acc@<id> (with merged values in merge_accuracy, #154) for ASAP, as@<id> for AutoSketch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zzylol
added a commit
that referenced
this pull request
Oct 6, 2026
The two cost models are gone (#145's weighted objective replaces them, in the runner of the PRs below). This keeps ASAP's one-time profiling time (#777 section 7) and documents how the reduced grid is run; results are committed after the full run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 6, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reworks
rqe-optimizer's cost model and MILP objective. Covers #143 S3, S7, S2 (plusmetric/grouping_labelsfrom S1) and half of S6.milp::minimize(..., Objective::AUCCost { w_cpu, w_mem })minimizesw_cpu · CPU + w_mem · memory_GiB, default(1, 0).minimize_tco,minimize_cost,MachineFamilyand the EC2 pricing snapshot are gone.MetricFacts { labels, scrape_interval_secs, cardinality }replacesLabelSetInfo.λis derived from it.(L − x)/y + 1.xandymust be multiples of the scrape interval.Also:
analytical_cost_model.rs(formerlyobjectives.rs) now holds the one copy of the cost formulas, whichscore()and the MILP share (they were duplicated before), and candidate dominance compares ingest memory and closed-window storage.Not run end to end:
small_problem --milpneeds a freshscripts/export_rqe_optimizer_costs.shrun. The older local table has noexact-increaserows, so the rate RQEs are unservable with it.--candidates-onlyruns.Cost model
Deployment with grouping labels
G, windowx, slidey. RQE with lookbackL, intervalT. Measured per-instance costsm,c_ins,c_mrg,c_qry.λ · (x/y) · c_inscard(G) · m · x/y(open windows)card(G) · (L/x − 1) · c_mrg / Tcard(G) · m(one accumulator per group), 0 whenL == xcard(G) · c_qry / Tcard(G) · 8 B; TopKcard(G) · 32 · 16 Bcard(G) · m · ((max L − x)/y + 1)(closed windows)λ = cardinality[metric.labels] / scrape_interval_secs.Shared constraints:$\sum_d z_{rd} = 1$ , $z_{rd} \le u_d$ , $u_d \le \sum_r z_{rd}$ . Latency bounds exclude pairs, so $z_{rd} = 0$ when $\text{lat}_{rd}$ exceeds the bound.
CPU is the mean, i.e. the area under the curve. Plans are sized for average load, not bursts.
Design decisions
Settled in a grilling session. Each entry gives the question and the answer as agreed.
Before grilling
card(G) · m, one accumulator per group, with all groups merged at once. The windows folded in are live objects already counted in storage, so they are not counted again. A direct query (L == x) merges nothing and has no merge memory.Gfor now. No regrouping of finer deployments.Tco, and renameCosttoAUCCost.Q1. Do S1 and S2 come into #145? S2 in full, plus only
metricandgrouping_labelsfrom S1. The Raqe rename, milliseconds,spatial_filterand the latency SLA on the query stay in S1.Q2. How are costs scaled with keys per instance? Per-family laws with
measured_keys. Superseded by Q15.Q3. Which parts of S4b come in? Shape (
PerGroup/Shared) × law (Fixed/PerKey) in. Mergeable/subtractable, the key tracker and HydraKLL stay in S4b. Shape × law later dropped by Q15.Q4. Formula for storage? Adopt the S7 formula: closed windows
(L − x)/y + 1, for the longestLthe deployment serves.Q5 / Q6. How do the phases feed the objective, and who provides the weights? Simplify the objective to
w_cpu · CPU + w_mem · memory, starting at(1, 0). Memory is option C: the sum of all memory terms, as if every query evaluates at once. The weights are machine-family constants namedw_cpuandw_mem.Q7. How does #145 relate to #141? Independently. #141 (
CostModel::Usage/Peak,minimize_model_cost) overlaps with this and will conflict inmilp.rs.Q8. What happens to
MachineFamily'svcpu,memory_gibandusd_per_hour? Drop them; no $/h reporting.Q9. Where do the weights come from? Option A: delete the EC2 pricing infrastructure (snapshot,
fetch_ec2_pricing.py,from_pricing_json,--machine-family). The weights are given directly.Q10. Is
MachineFamilystill needed? No, delete it. The weights live inObjective::AUCCost { w_cpu, w_mem }.Q11. Should merge and query CPU scale with keys for PerKey families? Agreed, scale merge and query too. Superseded by Q15.
Q12. Is
K(key labels) needed, and is the top-k heap modeled? SkipK, since no in-scope family is PerKey with non-emptyK. Treat top-k as fixed at the benchmark'sCMS_HEAP_TOP_K = 32: the heap is excluded from memory and included in CPU.Q13. What is the S2 data model? Final shape:
MetricFacts { labels, scrape_interval_secs, cardinality: BTreeMap<LabelSet, u64> }, keyed by metric name.λ = cardinality[labels] / scrape_interval, derived only, no override. This reverses Tracking: make rqe-optimizer the solver for ASAPQuery's planner #143's "arrival rate is an explicit per-metric input".RqeandDeploymentcarrymetricandgrouping_labels. Eligibility and sharing match on(capability, metric, grouping_labels).xandymust be multiples of the scrape interval (half of S6).G ⊄ labelsGor forlabelscard = 0orscrape = 0card(G) > card(labels)Q14. What do the scored output and the Pareto front look like?
(cpu, memory, {latency}).Objectivesbecomes per phase:ingest,merge,queryandstorage, each aPhaseCost { cpu_secs_per_sec, memory_bytes }, plusquery_latency_secs. It hascpu()andmemory_bytes()totals. Later renamedPlanCost, inanalytical_cost_model.rs(formerlyobjectives.rs), since it is a plan's cost andObjectivealready names what the MILP minimizes.peak_query_memory_bytes,retained_memory_bytesandtco_cpu_secs_per_sec.w_memis per GiB.Q15. Drop shape × law and
measured_keys? Yes. The export already divides the exact accumulators' memory and merge bygroups_per_instance(aqpbm-core/src/atomic_costs.rs), socard(G) × mis already correct for every in-scope family. Defer both to #142, where HydraKLL (Shared + Fixed) first needs them.Q16. How many bytes per output entry for query memory? Constants: 8 B per value, and 16 B per top-k (key, count) entry, with 32 entries per group.
Q17. Final scope
In #145:
AUCCostobjectivemetricandgrouping_labelsObjectives, and the Pareto front keptNot in #145:
Kand top-kkscalingOverlap
milp.rs. Whichever PR merges second has to reconcile the two.λis reversed here; a comment on Tracking: make rqe-optimizer the solver for ASAPQuery's planner #143 explains it.Test plan
w_memflips the choice between a CPU-heavy and a memory-heavy deploymentL == x), and storage sized by the longest lookback of a shared deploymentcargo test --workspace,cargo clippy --workspace --all-targets -- -D warnings(pre-commit)small_problem --milpon a fresh cost export🤖 Generated with Claude Code