Repository navigation
Conversation
…on one deployment Adds the benefit binary: a seeded generator of the Kubernetes control-plane series and the planner-layering examples' metrics, a survey of the apiserver, etcd and kubelet rules from a rule trace, and an experiment that plans a workload, runs it once on Prometheus alone and once on the data plane executing the selected plan, and compares answers (Prometheus, the Planner executor reference, accuracy targets), CPU, memory and latency. control_plane::plan_and_select returns Stage 3's selection record with the plan. The data plane gets --execution-memory-limit-mib (default 64, the executor's default).
Remote Write requests carry at most 10,000 series, under the data plane's body limit. --only-rules runs a jointly plannable subset of the kept rules. Adds the sketch-bench measured operation costs and the results of the examples (10x, 50x, 50x without raw data) and kube (10x) experiments, the rule survey, the joint-planning measurements and a data-plane CPU profile.
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.
Why
The design doc's benefit experiment runs one workload twice on one deployment: (a) Prometheus alone answers every query exactly at query time; (b) the backend executes ASAPPlanner's selected plan, with Remote Write going to the data plane and everything the plan does not answer forwarded. The goal is absolute CPU, memory and latency for both runs, the error against the exact answer, and Stage 3's predicted usage next to the measured one. Before this PR the backend had a correctness end-to-end test (#822) but no way to run a workload at scale or measure anything.
What
benefit(bench/), with three subcommands:rulessurveys a rule trace. It keeps the apiserver, etcd and kubelet rule groups and checks each rule alone: does the Planner plan it, does the data plane compile the plan, and does one evaluation over generated samples run? It writesrules.json, with the reason for every dropped rule.planwrites the Planner workload, the selected plan, the same plan with nothing materialized (the correctness reference), and Stage 3's selection record.experimentplans, runs the baseline and the ASAP run, compares them, and writesreport.jsonandsummary.md.bench/src/generator.rs). It covers every series the rules read: Kubernetes control-plane families with realistic label sets, counters that only rise and reset with probability 1e-4 per sample, classic histograms (_bucketwithle,_sum,_count) and gauges. It also covers the examples'http_requests_totalandlatency_ms. The scale is set by--scale(it multiplies the instance, node and peer dimensions),--sample-interval-ms,--duration-minutesand--seed.DataWorkloadfacts per metric (series count, sample interval, declared type) andmax_distinct_per_evaluationare computed from the generated series. Every series exists for the whole run, so the bound is the metric's series count.--prices illustrative | aws-fargate-us-east-1, under cost model 1. The backend pin (58999df) predates the Fargate preset, so the flag reproduces ASAPPlanner main 4ef9f3e5'sResourceUnitCost::aws_fargate_us_east_1(speed)constants ($0.000011244/vCPU-s, $0.000001235/GiB-s). Replace it with the Planner function at the next re-pin.bench/inputs/measured_operation_costs.json, 138 entries. It is sketch-bench main a3753f2's study table (rqe-optimizer/results/autosketch-vs-asap-hydra/inputs/saturation/optimizer_cost/rqe_atomic_costs.json, last changed in 3fa808b, asap_sketchlib 0.3.0), converted by the pinned Planner'sasap-devtools convert_rqe_atomic_costs. That tool skipped the exact accumulators and the Hydra rows. It is passed through--measured-operation-costs.control_plane::plan_and_select:plan_workloadplus Stage 3'sSelection, so the harness can report predicted usage.data_plane --execution-memory-limit-mib(default 64, the executor's own default). The hard-coded 64 MiB working-memory limit forwarded every Example 4b evaluation at 10× scale. The experiments use 4096.bench/results/: raw JSON and a summary table per experiment, the rule survey, the joint-planning measurements, and a data-plane CPU profile.How
One
experimentdoes the following:It plans the workload in-process: Stages 0–3 with generator facts and the chosen prices and measured costs.
It starts Prometheus 3.5.0, the release binary pinned by version and sha256, with
--web.enable-remote-write-receiver. In the ASAP run it also starts the releasedata_planewith the plan. Each process is pinned withtaskset.It generates samples tick by tick (virtual time from 2026-01-01T00:00Z, as fast as the processes accept them). Each tick goes out as Remote Write in requests of at most 10,000 series: to Prometheus in the baseline, and to both Prometheus and the data plane in the ASAP run, because the data plane reads raw ranges from Prometheus.
After each tick it evaluates every query at each of its interval ticks in
(previous tick, tick], from--eval-start-minuteson. These are instant queries atT = start + k·interval, as Prometheus rule evaluation would issue them. In the ASAP run they go to the data plane, which answers planned queries (x-asap-execution: plan) and forwards the rest.A sampler reads
/proc/<pid>/statandVmRSSevery 250 ms. CPU is the cumulative utime+stime at exit, startup included.It compares each ASAP evaluation with the baseline's answer at the same
T, and both with the Planner executor running the reference plan over the generated samples (compare::Reference). It checks every answer against the query's accuracy target:When the plan's labels differ from Prometheus', both answers are compared on the label names they share, and the row is counted under "Labels differ".
Key code interfaces added / modified
report.json:scale,series_per_metric,deployment_inputs,selection(selected id, node materializations, predicted usage of the selected and the nothing-materialized candidate, the five cheapest candidates),runs[](per-processcpu_core_seconds,peak_rss_gib,mean_rss_gib, wall seconds, Prometheus version) andqueries[](evaluations, answered by plan, forward reasons, baseline = reference, plan = reference, meets target, labels differ, max abs/rel/rank error, p50/p99 latency per run).Usage (the 50× examples run):
Before this PR
There were no benefit numbers. Correctness was checked only in
e2e_examples.rs, against a stand-in Prometheus, on a few series.After this PR
These are measured results. All runs are on a 56-core x86 server, Prometheus on cores 0–3 and the data plane on cores 4–7, release builds. Prometheus is 3.5.0 (left-open ranges, matching the Planner). Prices are AWS Fargate us-east-1 under cost model 1 with the measured operation costs. The data is 90 minutes at one sample per 15 s per series, seed 1, and queries are evaluated from minute 10.
Correctness first. Every planned answer equals the Planner executor reference, and every baseline answer equals it too (exact targets, or within the rank target):
Example 1's
topk by (job)answers have the right values in all 481 evaluations of each run, but without theinstancelabel Prometheus keeps (see Planner gaps). The quantile queries' worst rank error is 0.0100, at the 0.01 target. KLL returns the window's top sample, rank 1.0, while Prometheus interpolates at 0.99; the value difference against Prometheus' interpolated p99 is up to 1,587 ms.Benefit, absolute numbers. CPU is in core-seconds and memory in GiB over the whole run:
Latency p50 / p99 in ms, baseline vs ASAP (50× runs):
sum by (job) (rate(...[1m]))topk by (job) (10, sum_over_time(...[1m]))quantile_over_time(0.99, latency_ms[5m])quantile_over_time(0.99, latency_ms[60m])Kubernetes rules, 50× (114,300 series), p50 / p99 ms:
cluster_verb_scope_le:apiserver_request_sli_duration_seconds_bucket:increase1h(every 180 s)The per-query tables for every experiment are in
bench/results/*/summary.md.Predicted vs measured (Stage 3, over the 90 minutes of data):
P141-m8: 12 nodesMatAtQueryKeptAcrossEvaluationsP141-m8P195-m40: 23 nodesMatAtIngestionP159-m485: 92 kept + 1MatAtQueryP159-m485The prediction covers the plan's operator work only. It does not cover Remote Write decoding, the HTTP and JSON round trip of every raw range, or Prometheus' own ingestion. Measured data-plane CPU is 400–1,500× the prediction. Memory is closer only where state is materialized (the no-raw-data run: 0.92 GiB predicted mean vs 1.36 GiB measured).
Where the data plane's CPU goes (
bench/results/profile-examples-s50/top30-self.txt,perf record -F 499 -gfor 120 s of the 50× examples ASAP run, self time). About 40% is glibcmalloc/free. About 15% is cloning and droppingVec<asap_executor::values::Value>rows.summary::executeandexecute_mergetake about 7%, andserde_jsonraw-range decoding about 2%. The build has no frame pointers, so caller attribution is not reliable.Result: no benefit at these scales. On the same deployment, the ASAP run uses more CPU, more memory and has higher latency than Prometheus alone in every configuration. Answers are correct throughout. Even when the data plane serves every query from ingestion-time panes (the no-raw-data what-if), Prometheus' query CPU drops by 65.6 core-s, but the data plane spends 250 core-s.
Workloads and rules
apiserver,etcdorkubelet). 13 are kept. The experiments run 6 of them, the largest set the Planner can plan jointly (see below): KubeAggregatedAPIErrors, KubeAggregatedAPIDown, KubeNodeNotReady, KubeletTooManyPods, KubeNodeEviction andcluster_verb_scope_le:apiserver_request_sli_duration_seconds_bucket:increase1h. All are exact, evaluated every 30 s or 180 s.bench/results/kube-rules/rules.json)code_resource:…:rate5m×2,code_verb:…:increase1h×4, 7 POST/PUT burn-rate rules, KubeNodePressure, KubeNodeUnreachable)=~,!~) fail at run time ("unsupported scalar expression: Compare … op: Regex")histogram_quantilerule: etcd fsync/commit/peer RTT, apiserver and kubelet quantile recordings, KubeletPodStartUpLatencyHigh)sum without (…))absent,changes,last_over_time,predict_linear, subqueries)Planner gaps found
Joint planning does not scale. The valid candidates of a workload are close to the product of its queries' (
bench/results/kube-rules/joint-planning.json):So the full kept rule set cannot be planned.
Joint planning lost the kept-across-evaluations plans. With illustrative prices, each of the 7 POST burn-rate rules planned alone selects
MatAtQueryKeptAcrossEvaluations; the seven selections cost 50.9 per second together, against 124.2 per second for the same plans with nothing materialized. Planned together, only 16 candidates are valid andP1(nothing materialized) is selected at 122.6 per second. Those rules are now dropped by the regex gap, but the planning result stands.topk by (job)drops the series labels. The PromQL frontend lowers the Example 1 query to output columns[job, topk_10]; Prometheus returns each top series with all its labels. The values match.with_promql_series_identitysucceeds on this root, so the labels are lost at lowering, not in the backend.The executor rejects regex matchers,
withoutgrouping,histogram_quantileand per-entity functions. It compiles or runs none of them (table above), and these make up most of the real rule set. A regex matcher passes compilation and fails only on the first evaluation, sorulesprobes one evaluation over generated samples.Stage 3's CPU prediction is far below measurement, by 400–1,500× (table above). Its cost model counts operator work only, so a materialized candidate is predicted to save CPU that the deployment then spends on ingest and transport.
No candidate exists for the kube rules without raw data: every instant selector scans raw data. This is expected, but it means no configuration of these rules runs entirely from ingestion-time state.
Limitations
--execution-memory-limit-mib 131072and--reuse-runs. At 4096 MiB, the harness's nothing-materialized reference for the 1-hour histogram rule ran out of executor memory in 26 of 27 evaluations, while the plan itself answered all 27 exactly, equal to Prometheus.What re-pinning to ASAPPlanner e12eca60 (#755) would simplify
control_plane::plan_and_select,plan_selected_at_query_timeand the-mid parsing inselection_summarybecomeasap_planner::plan_installable, which returnsInstallablePlan { dag, queries, stored_outputs, query_time_raw_windows, selection }.Sds::from_dag) and the query engine's walk fromTimeRangetoScanwith offsets (raw_input,program) would readstored_outputs { built_at, retention_ms, pane window }andquery_time_raw_windowsinstead of re-deriving them.precompute::selectandscan_belowwould useasap_executor::RawSampleSelector.--prices aws-fargate-us-east-1reproduction becomesResourceUnitCost::aws_fargate_us_east_1, from 4ef9f3e5, which is already older than e12eca60.Verification
cargo test --release -p benefit: the generator is deterministic for a seed, counters never decrease without a reset, and histogram buckets are cumulative with+Inf=_count; the rule parser separates metric names from recording-rule outputs.cargo fmt --all -- --checkandcargo clippy --release -p benefit -p data_plane -p control_planeare clean. No CI ran (draft).bench/results/*/report.json).