From e0656b4cf13626c83622cc53ebbf82f0a0c34ee3 Mon Sep 17 00:00:00 2001 From: discord9 <55937128+discord9@users.noreply.github.com> Date: Thu, 10 Sep 2026 14:53:08 +0800 Subject: [PATCH] test(perf): add monotonic quantile regression controls Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> --- .../query_perf_fixture/prom_remote_write.rs | 24 +++++- .../promql_quantile_ascending/case.toml | 83 +++++++++++++++++++ .../promql_quantile_descending/case.toml | 83 +++++++++++++++++++ .../promql_quantile_selection/case.toml | 4 +- 4 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 tests/perf/query_cases/promql_quantile_ascending/case.toml create mode 100644 tests/perf/query_cases/promql_quantile_descending/case.toml diff --git a/src/cmd/src/bin/query_perf_fixture/prom_remote_write.rs b/src/cmd/src/bin/query_perf_fixture/prom_remote_write.rs index 1f36b0c2d2..4f45f06a11 100644 --- a/src/cmd/src/bin/query_perf_fixture/prom_remote_write.rs +++ b/src/cmd/src/bin/query_perf_fixture/prom_remote_write.rs @@ -45,7 +45,7 @@ pub(super) struct PromRemoteWriteArgs { value_pattern: ValuePattern, #[arg(long, default_value_t = 0.0)] value_base: f64, - #[arg(long, default_value_t = 0.125)] + #[arg(long, default_value_t = 0.125, allow_negative_numbers = true)] value_step: f64, #[arg(long, default_value_t = 97)] value_cardinality: u64, @@ -211,8 +211,16 @@ fn splitmix64(mut value: u64) -> u64 { mod tests { use std::collections::HashSet; + use clap::Parser; + use super::*; + #[derive(Parser)] + struct Cli { + #[command(flatten)] + prom_remote_write: PromRemoteWriteArgs, + } + fn args(pattern: ValuePattern) -> PromRemoteWriteArgs { PromRemoteWriteArgs { endpoint: "http://127.0.0.1:4000/v1/prometheus/write".to_string(), @@ -239,6 +247,20 @@ mod tests { } } + #[test] + fn value_step_accepts_a_separate_negative_argument() { + let cli = Cli::try_parse_from([ + "query_perf_fixture", + "--metric", + "fixture_metric", + "--value-step", + "-1", + ]) + .expect("negative value step should parse"); + + assert_eq!(cli.prom_remote_write.value_step, -1.0); + } + #[test] fn seeded_random_is_deterministic_in_range_and_highly_distinct() { let mut args = args(ValuePattern::SeededRandom); diff --git a/tests/perf/query_cases/promql_quantile_ascending/case.toml b/tests/perf/query_cases/promql_quantile_ascending/case.toml new file mode 100644 index 0000000000..d8de1fd53f --- /dev/null +++ b/tests/perf/query_cases/promql_quantile_ascending/case.toml @@ -0,0 +1,83 @@ +# PromQL quantile-over-time ascending-value negative control. +# +# Values are strictly increasing by global sample ordinal. This uses the +# Prometheus remote-write/metric-engine ingestion and storage path, whereas +# promql_quantile_selection uses direct readable SSTs. Both use 128 series, +# 780 samples per series, 15s cadence, and the same evaluation queries. + +[case] +name = "promql_quantile_ascending" +description = "PromQL quantile_over_time q=0.9 ascending-value remote-write negative control" + +[scenario] +kind = "prom_remote_write_then_query" + +[scenario.remote_write] +database = "public" +metric = "promql_quantile_ascending" +series_count = 128 +samples_per_series = 780 +sample_chunk_size = 780 +flush_every_sample_chunks = 1 +start_unix_millis = 1704067200000 +step_millis = 15000 +chunk_series_count = 128 + +[scenario.remote_write.value] +pattern = "unique" +base = 0 +step = 1 + +[scenario.remote_write.prom_store] +pending_rows_flush_interval = "1s" +max_batch_rows = 100000 + +# Selector and response-path control; it does not cover quantile behavior. +[[scenario.queries]] +name = "plain_selector_control_2h" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') promql_quantile_ascending{host=~'host.*'}" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +[[scenario.queries]] +name = "quantile_over_time_q90_1m" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') quantile_over_time(0.9, promql_quantile_ascending{host=~'host.*'}[1m])" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +[[scenario.queries]] +name = "quantile_over_time_q90_5m" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') quantile_over_time(0.9, promql_quantile_ascending{host=~'host.*'}[5m])" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +[[scenario.queries]] +name = "quantile_over_time_q90_1h" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') quantile_over_time(0.9, promql_quantile_ascending{host=~'host.*'}[1h])" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +# host0000 is series ordinal 0. The 1h range at 1704070800 excludes the left +# bound, spanning ordinals 1..240; q=0.9 rank 0.9 * (240 - 1) = 215.1 yields 216.1. +[[scenario.queries]] +name = "quantile_over_time_q90_1h_host0000_eval" +kind = "tql" +query = "TQL EVAL (1704070800, 1704070800, '15s') quantile_over_time(0.9, promql_quantile_ascending{host='host0000'}[1h])" +warmup = 0 +iterations = 1 diff --git a/tests/perf/query_cases/promql_quantile_descending/case.toml b/tests/perf/query_cases/promql_quantile_descending/case.toml new file mode 100644 index 0000000000..a4b2603c9d --- /dev/null +++ b/tests/perf/query_cases/promql_quantile_descending/case.toml @@ -0,0 +1,83 @@ +# PromQL quantile-over-time descending-value negative control. +# +# Values are strictly decreasing by global sample ordinal. This uses the +# Prometheus remote-write/metric-engine ingestion and storage path, whereas +# promql_quantile_selection uses direct readable SSTs. Both use 128 series, +# 780 samples per series, 15s cadence, and the same evaluation queries. + +[case] +name = "promql_quantile_descending" +description = "PromQL quantile_over_time q=0.9 descending-value remote-write negative control" + +[scenario] +kind = "prom_remote_write_then_query" + +[scenario.remote_write] +database = "public" +metric = "promql_quantile_descending" +series_count = 128 +samples_per_series = 780 +sample_chunk_size = 780 +flush_every_sample_chunks = 1 +start_unix_millis = 1704067200000 +step_millis = 15000 +chunk_series_count = 128 + +[scenario.remote_write.value] +pattern = "unique" +base = 0 +step = -1 + +[scenario.remote_write.prom_store] +pending_rows_flush_interval = "1s" +max_batch_rows = 100000 + +# Selector and response-path control; it does not cover quantile behavior. +[[scenario.queries]] +name = "plain_selector_control_2h" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') promql_quantile_descending{host=~'host.*'}" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +[[scenario.queries]] +name = "quantile_over_time_q90_1m" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') quantile_over_time(0.9, promql_quantile_descending{host=~'host.*'}[1m])" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +[[scenario.queries]] +name = "quantile_over_time_q90_5m" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') quantile_over_time(0.9, promql_quantile_descending{host=~'host.*'}[5m])" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +[[scenario.queries]] +name = "quantile_over_time_q90_1h" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') quantile_over_time(0.9, promql_quantile_descending{host=~'host.*'}[1h])" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +# host0000 is series ordinal 0. At 1704070800, the 1h range is ordinals 1..240; +# sorted values are -240..-1, so rank 215.1 interpolates -25 to -24 as -24.9. +[[scenario.queries]] +name = "quantile_over_time_q90_1h_host0000_eval" +kind = "tql" +query = "TQL EVAL (1704070800, 1704070800, '15s') quantile_over_time(0.9, promql_quantile_descending{host='host0000'}[1h])" +warmup = 0 +iterations = 1 diff --git a/tests/perf/query_cases/promql_quantile_selection/case.toml b/tests/perf/query_cases/promql_quantile_selection/case.toml index d86314ef41..37bb2fd72b 100644 --- a/tests/perf/query_cases/promql_quantile_selection/case.toml +++ b/tests/perf/query_cases/promql_quantile_selection/case.toml @@ -1,7 +1,7 @@ # PromQL quantile-over-time latency regression coverage. # -# This is wave-only end-to-end coverage. Ascending- and descending-value -# microbenchmark controls remain separately required. +# This is wave-only end-to-end coverage. The strictly monotonic remote-write +# controls are promql_quantile_ascending and promql_quantile_descending. [case] name = "promql_quantile_selection"