diff --git a/tests/perf/query_cases/promql_series_rate_fusion/README.md b/tests/perf/query_cases/promql_series_rate_fusion/README.md new file mode 100644 index 00000000000..86d6d9fef93 --- /dev/null +++ b/tests/perf/query_cases/promql_series_rate_fusion/README.md @@ -0,0 +1,50 @@ +# PromQL series rate-fusion regression fixture + +This native Query Regression case isolates the candidate shape `SUM(Float64)` by +`dc` over one direct `prom_rate` expression, projected as Float64 with a +millisecond timestamp and zero offset. The immutable input uses the previously qualified dictionary-tag A direct-SST +generator, schema, and layout. All parameters are specified in this case; no +private or gitignored source file is required. It uses seed 25652, 4,096 +instances × 512 samples = 2,097,152 rows, 16 `dc` values, 4 devices, and two flat +non-overlapping SSTs of 1,048,576 rows each, 8,192-row groups, 15-second +millisecond timestamps, primary key `(instance, dc, device)`, and deterministic +wave values from 0 through 100. It uses the same data generator, not the +original 127-slope counter or an archived original input. + +The range window is 1704069900–1704071715 seconds at 15-second steps (122 +evaluations), fully covered by fixture timestamps from 1704067200 through +1704074865. The first SST ends at 1704071025; the 5-minute lookback begins at +1704069600, so this range crosses the actual two-SST boundary and requests data +from both files. Actual scanned row counts must be audited from runtime plans; +the full 2,097,152-row fixture size is not an expected scan count for this query. +The primary range query expects 16 `dc` output groups per evaluation (1,952 +result rows) and evaluates 4,096 series per evaluation (499,712 +evaluation-points). The instant query at 1704071715 expects 16 output rows. +Primary `TQL EVAL` entries preserve complete response data and use 3 warmups/20 +range iterations and 3 warmups/9 instant iterations; the range EVAL is also the +runner's untimed first-query validation. Separate one-shot `TQL ANALYZE VERBOSE` +entries capture runtime gate, actual plan, scan/index, and work-count diagnostics +without adding analyze overhead to timed samples. An untimed one-iteration SQL +audit returns one row per `dc`, verifying 131,072 rows and 256 instances per +group and min/max timestamps 1704067200 and 1704074865. + +Controls are limited to grouped `sum_over_time` (wider non-rate path), +grouped `min by (dc) (rate(...))` (a different aggregation operator and a +negative fusion-gate control), and grouped `rate` with a 15-second offset +(offset gate). MIN keeps the same range, rate-window work, and compact 1,952-row +answer while not matching the SUM candidate. They reuse the same table and +range. The offset query's parser acceptance must be confirmed on the pinned +runtime. Each timed query retains the existing 10% candidate-latency-regression +threshold: a pass only means that configured guard did not trip; it is not proof +of candidate-gate activation or performance improvement. Do not loosen it to +hide noise. + +After a native run, audit the normalized plans and complete raw responses, +including group labels, timestamps, numeric values with appropriate floating +point tolerance, and expected row counts. Also audit source SHAs, compiler, +features, fixture layout, the actual runtime gate, scans/indexes, and work counts. +Official performance acceptance still needs clean base/candidate medians, +control behavior, drift and tolerance review, and complete cross-arm answers. +This fixture alone cannot establish a gain. Finite-pool behavior, drops, and +spill behavior belong to code tests, not this CI case. No official workflow was +run as part of adding this fixture. diff --git a/tests/perf/query_cases/promql_series_rate_fusion/case.toml b/tests/perf/query_cases/promql_series_rate_fusion/case.toml new file mode 100644 index 00000000000..3eb5a1998a3 --- /dev/null +++ b/tests/perf/query_cases/promql_series_rate_fusion/case.toml @@ -0,0 +1,144 @@ +# Minimal native Query Regression fixture for the narrow direct-prom-rate sum fusion candidate. +# This deterministic wave is not the original 127-slope counter/input archive. +# Retain complete raw responses for parity auditing; passing thresholds only means +# the configured latency guard did not trip, not that fusion or a speedup was proven. + +[case] +name = "promql_series_rate_fusion" +description = "PromQL rate-only grouped sum fusion regression fixture" + +[scenario] +kind = "direct_readable_sst" +seed = 25652 + +[[scenario.tables]] +database = "public" +name = "promql_series_rate_fusion" +engine = "mito" +append_mode = true +sst_format = "flat" +primary_key = ["instance", "dc", "device"] +time_index = "ts" + +[[scenario.tables.columns]] +name = "instance" +type = "STRING" +semantic = "tag" +distribution = { kind = "cardinality", values = 4096, prefix = "instance" } + +[[scenario.tables.columns]] +name = "dc" +type = "STRING" +semantic = "tag" +distribution = { kind = "cardinality", values = 16, prefix = "dc" } + +[[scenario.tables.columns]] +name = "device" +type = "STRING" +semantic = "tag" +distribution = { kind = "cardinality", values = 4, prefix = "device" } + +[[scenario.tables.columns]] +name = "val" +type = "DOUBLE" +semantic = "field" +distribution = { kind = "deterministic_wave", min = 0.0, max = 100.0 } + +[[scenario.tables.columns]] +name = "ts" +type = "TIMESTAMP(3)" +semantic = "timestamp" + +[scenario.layout] +regions = 1 +sst_count = 2 +rows_per_sst = 1048576 +row_group_size = 8192 +series_count = 4096 +start_unix_nanos = 1704067200000000000 +step_nanos = 15000000000 +time_range_layout = "non_overlapping_per_sst" +series_layout = "timestamp_major" + +# 122 evaluations * 16 dc groups = 1,952 output rows. Each range evaluates +# 4,096 series at 122 timestamps (499,712 evaluated series points). This range +# crosses the first-SST boundary (1704071025); its initial 5m lookback starts +# at 1704069600, ensuring both SSTs are in the requested time span. +[[scenario.queries]] +name = "grouped_rate_sum_range" +kind = "tql" +query = "TQL EVAL (1704069900, 1704071715, '15s') sum by (dc) (rate(promql_series_rate_fusion[5m]))" +warmup = 3 +iterations = 20 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +# Same complete response shape at the latest timestamp: 16 groups. +[[scenario.queries]] +name = "grouped_rate_sum_instant" +kind = "tql" +query = "TQL EVAL (1704071715, 1704071715, '15s') sum by (dc) (rate(promql_series_rate_fusion[5m]))" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +# Separate diagnostics are intentionally not included in timed samples. +[[scenario.queries]] +name = "analyze_grouped_rate_sum_range" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704069900, 1704071715, '15s') sum by (dc) (rate(promql_series_rate_fusion[5m]))" +warmup = 0 +iterations = 1 + +[[scenario.queries]] +name = "analyze_grouped_rate_sum_instant" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704071715, 1704071715, '15s') sum by (dc) (rate(promql_series_rate_fusion[5m]))" +warmup = 0 +iterations = 1 + +# Wider/non-rate path: does not match the direct rate-only fusion candidate. +[[scenario.queries]] +name = "grouped_sum_over_time_control" +kind = "tql" +query = "TQL EVAL (1704069900, 1704071715, '15s') sum by (dc) (sum_over_time(promql_series_rate_fusion[5m]))" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +# MIN is a distinct aggregation operator, so it must not match the SUM fusion +# candidate. It retains the same rate-window work and compact grouped response. +[[scenario.queries]] +name = "grouped_rate_min_control" +kind = "tql" +query = "TQL EVAL (1704069900, 1704071715, '15s') min by (dc) (rate(promql_series_rate_fusion[5m]))" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10 + +# Complete-table materialization audit: 16 rows, each dc has 131072 rows and +# 256 instances, with timestamps spanning the entire two-SST fixture. +[[scenario.queries]] +name = "validate_materialized_data" +kind = "sql" +query = "SELECT dc, COUNT(*) AS row_count, COUNT(DISTINCT instance) AS series_count, MIN(ts) AS min_ts, MAX(ts) AS max_ts FROM promql_series_rate_fusion GROUP BY dc ORDER BY dc" +warmup = 0 +iterations = 1 + +# Offset gate control, if accepted by this pinned parser; an offset is not fused. +[[scenario.queries]] +name = "grouped_rate_sum_offset_control" +kind = "tql" +query = "TQL EVAL (1704069900, 1704071715, '15s') sum by (dc) (rate(promql_series_rate_fusion[5m] offset 15s))" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 10