diff --git a/.github/scripts/query-regression-run.py b/.github/scripts/query-regression-run.py index d293aa3e0c9..268cb0bae6c 100644 --- a/.github/scripts/query-regression-run.py +++ b/.github/scripts/query-regression-run.py @@ -38,6 +38,7 @@ DEFAULT_CASES = [ "tests/perf/query_cases/prom_remote_write_mixed_every/case.toml", "tests/perf/query_cases/prom_remote_write_integer_counter/case.toml", "tests/perf/query_cases/promql_range_boundary/case.toml", + "tests/perf/query_cases/promql_instant_last_row_9034/case.toml", ] HEAVY_CASES = [ diff --git a/tests/perf/README.md b/tests/perf/README.md index 42fd8c286e0..d518fec996c 100644 --- a/tests/perf/README.md +++ b/tests/perf/README.md @@ -204,7 +204,7 @@ case for issue #7913. It writes 8192 series × 20160 samples through remote-writ in 1440-sample daily time chunks, flushing after each chunk before running 1d/7d/14d TQL selectors. It is not included in the default `all` case set because ingestion cost dominates routine CI validation. Commenting `/query-regression heavy` runs -only this case; `/query-regression` runs the six routine default cases. Manual +only this case; `/query-regression` runs the seven routine default cases. Manual workflow dispatch accepts the `heavy` token to select this case. ## OTLP trace load scenario @@ -393,12 +393,12 @@ parquetbench/scanbench` as the read-bench tool against each target's data direct The workflow runs when an allowlisted repository admin comments `/query-regression` on a non-draft PR. It does not rerun on pushes, -ready-for-review, or reopen events. `/query-regression` runs the six routine -default cases; `/query-regression heavy` runs only the high-cardinality -remote-write #7913 case. PR runs build base/candidate once and -use `--allow-large-fixture`. Manual `workflow_dispatch` runs can pass `all`, -`heavy`, one case path, or a comma/whitespace-separated list of case paths, and -can override refs. +ready-for-review, or reopen events. `/query-regression` runs the seven routine +default cases, including `promql_instant_last_row_9034`; +`/query-regression heavy` runs only the high-cardinality remote-write #7913 case. +PR runs build base/candidate once and use `--allow-large-fixture`. Manual +`workflow_dispatch` runs can pass `all`, `heavy`, one case path, or a +comma/whitespace-separated list of case paths, and can override refs. Comment admission is two workflows. `slash-command-dispatch.yml` uses [peter-evans/slash-command-dispatch](https://github.com/peter-evans/slash-command-dispatch) diff --git a/tests/perf/query_cases/promql_instant_last_row_9034/case.toml b/tests/perf/query_cases/promql_instant_last_row_9034/case.toml new file mode 100644 index 00000000000..6dd5dc2fb8b --- /dev/null +++ b/tests/perf/query_cases/promql_instant_last_row_9034/case.toml @@ -0,0 +1,92 @@ +# PromQL instant-selector latency regression coverage for #9034. +# +# Instant, range, and aggregate controls cover LastRow latency behavior. The +# candidate instant query may enable the existing LastRow selector; the range +# query must not. This is latency regression coverage only: optimizer and +# sqlness tests own plan and correctness coverage. Evaluation is 0.7s after the +# final generated sample (last timestamp = start + 1023 * 0.1s = +# 1704067302.3) and remains inside the default lookback window. + +[case] +name = "promql_instant_last_row_9034" +description = "PromQL instant selector LastRow latency regression for #9034" + +[scenario] +kind = "direct_readable_sst" +seed = 9034 + +[[scenario.tables]] +database = "public" +name = "promql_instant_last_row_9034" +engine = "mito" +append_mode = true +sst_format = "flat" +primary_key = ["host", "instance"] +time_index = "ts" + +[[scenario.tables.columns]] +name = "host" +type = "STRING" +semantic = "tag" +distribution = { kind = "cardinality", values = 16, prefix = "host" } + +[[scenario.tables.columns]] +name = "instance" +type = "STRING" +semantic = "tag" +distribution = { kind = "cardinality", values = 256, prefix = "instance" } + +[[scenario.tables.columns]] +name = "value" +type = "DOUBLE" +semantic = "field" +distribution = { kind = "deterministic_wave", min = 0.0, max = 1000.0 } + +[[scenario.tables.columns]] +name = "ts" +type = "TIMESTAMP(9)" +semantic = "timestamp" + +[scenario.layout] +regions = 1 +sst_count = 64 +rows_per_sst = 4096 +row_group_size = 1024 +series_count = 256 +start_unix_nanos = 1704067200000000000 +step_nanos = 100000000 +time_range_layout = "non_overlapping_per_sst" +series_layout = "timestamp_major" + +# Candidate instant query may enable the existing LastRow selector. +[[scenario.queries]] +name = "instant_selector_last_row_candidate" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704067303, 1704067303, '1s') promql_instant_last_row_9034{host=~'host.*'}" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 25 + +# True range control: this must not enable the LastRow selector. +[[scenario.queries]] +name = "range_selector_last_row_control" +kind = "tql" +query = "TQL ANALYZE VERBOSE (1704067293, 1704067303, '1s') promql_instant_last_row_9034{host=~'host.*'}" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 25 + +# This pre-existing aggregate-hint control should return one row per 256 series; it is latency regression coverage, not a result oracle. +[[scenario.queries]] +name = "sql_aggregate_last_row_control" +kind = "sql" +query = "SELECT host, instance, last_value(value ORDER BY ts ASC) FROM promql_instant_last_row_9034 GROUP BY host, instance" +warmup = 3 +iterations = 9 + +[scenario.queries.thresholds] +max_candidate_latency_regression_pct = 25 diff --git a/tests/perf/test_query_regression_case_selection.py b/tests/perf/test_query_regression_case_selection.py index 642b6ee1f2b..84b3cd44fa7 100644 --- a/tests/perf/test_query_regression_case_selection.py +++ b/tests/perf/test_query_regression_case_selection.py @@ -40,6 +40,7 @@ class QueryRegressionCaseSelectionTest(unittest.TestCase): "tests/perf/query_cases/prom_remote_write_mixed_every/case.toml", "tests/perf/query_cases/prom_remote_write_integer_counter/case.toml", "tests/perf/query_cases/promql_range_boundary/case.toml", + "tests/perf/query_cases/promql_instant_last_row_9034/case.toml", ], )