Repository navigation
Description
User need and MVP
Different levels of tests with targeted queries:
- the queries can be compiled by ASAPQuery-backend and ASAPPlanner, the outputs are the correct physical plans
- The queries/physical plans can be executed by the ASAPQuery-backend data plane, and all the mapped physical operators in ASAP are implemented.
- The execution of such queries and physical plans by ASAPQuery-backend data plane shows lower resource usage and lower query latency compared to exact DB, i.e., Prometheus, VictoriaMetrics, ClickHouse.
Authoritative design
Adding e2e tests for that 3 levels.
Inputs, outputs, and invariants
Evaluation queries: PromQL and ClickHouse SQL
Use a 100 ms scrape interval. Vary label_0 cardinality across 10, 100, 1,000, 10,000, 100,000, and 1,000,000, and vary T across 1m, 10m, 1h, 6h, and 24h. Evaluate instant queries every 1 second and temporal queries every 1 minute. For quantiles, use q = 0.50, 0.75, 0.90, 0.95, and 0.99. Exercise these queries at all three test levels above.
Use a ClickHouse samples table with series_id UInt64, label_0 String, ts_ms Int64 (Unix milliseconds), and value Float64; retain the complete label set in a series dictionary keyed by series_id. Bind {t_ms:Int64} to each evaluation timestamp, {window_ms:Int64} to T in milliseconds, and {q:Float64} to the requested quantile. Each SQL statement starts with the relevant CTEs below. The examples assume unique timestamps per series, finite float samples, no stale markers, and no native histograms. Those additional PromQL semantics need dedicated fixtures and ClickHouse handling before claiming full parity. Keep the benchmark input and evaluation timestamps identical across engines.
Shared CTEs for instant and range-vector input (use the relevant one before each query):
WITH
{t_ms:Int64} AS t_ms,
{window_ms:Int64} AS window_ms,
instant_samples AS (
SELECT series_id, label_0, argMax(value, ts_ms) AS value
FROM samples
WHERE ts_ms <= t_ms AND ts_ms >= t_ms - 300000
GROUP BY series_id, label_0
),
window_samples AS (
SELECT series_id, label_0, ts_ms, value
FROM samples
WHERE ts_ms > t_ms - window_ms AND ts_ms <= t_ms
)
SELECT ...;The 300000 ms instant lookback must match the configured Prometheus lookback. If the fixture includes stale markers, argMax alone is insufficient: inspect the latest sample's stale flag before admitting a series. Apply label predicates within these CTEs. Preserve the series dictionary when comparing output labels.
For queries 6, 7, and 9, append these CTEs after window_samples and use the appropriate final SELECT. They implement Prometheus 3.5.0's float-counter reset correction and extrapolation in ClickHouse syntax; increase_value = rate_value * window_ms / 1000 except for normal floating-point rounding.
, ordered AS (
SELECT series_id, label_0, ts_ms, value,
row_number() OVER (PARTITION BY series_id ORDER BY ts_ms) AS sample_index,
lag(value, 1, 0.) OVER (PARTITION BY series_id ORDER BY ts_ms) AS previous_value
FROM window_samples
), corrected AS (
SELECT series_id, label_0, count() AS sample_count,
min(ts_ms) AS first_ms, max(ts_ms) AS last_ms,
argMin(value, ts_ms) AS first_value,
argMax(value, ts_ms) AS last_value,
sum(if(sample_index > 1 AND value < previous_value,
previous_value, 0.)) AS reset_correction
FROM ordered
GROUP BY series_id, label_0
HAVING sample_count >= 2
), durations AS (
SELECT *,
last_value - first_value + reset_correction AS corrected_delta,
(last_ms - first_ms) / 1000. AS sampled_seconds,
(first_ms - (t_ms - window_ms)) / 1000. AS gap_start_seconds,
(t_ms - last_ms) / 1000. AS gap_end_seconds
FROM corrected
), thresholds AS (
SELECT *, sampled_seconds / (sample_count - 1) AS average_gap_seconds
FROM durations
), adjusted AS (
SELECT *,
if(gap_start_seconds >= 1.1 * average_gap_seconds,
average_gap_seconds / 2, gap_start_seconds) AS start_seconds,
if(gap_end_seconds >= 1.1 * average_gap_seconds,
average_gap_seconds / 2, gap_end_seconds) AS end_seconds
FROM thresholds
), extrapolated AS (
SELECT *,
if(corrected_delta > 0 AND first_value >= 0,
least(start_seconds, sampled_seconds * first_value / corrected_delta),
start_seconds) AS zero_adjusted_start_seconds
FROM adjusted
), per_series_counter AS (
SELECT series_id, label_0,
corrected_delta *
(sampled_seconds + zero_adjusted_start_seconds + end_seconds)
/ sampled_seconds AS increase_value,
corrected_delta *
(sampled_seconds + zero_adjusted_start_seconds + end_seconds)
/ sampled_seconds / (window_ms / 1000.) AS rate_value
FROM extrapolated
)
SELECT ...;Replace SELECT ... with the matching final query below. In #2 and #9, ClickHouse ORDER BY label_0, value DESC ... LIMIT 3 BY label_0 ranks rows and keeps up to three original series in each label_0 group. This is the ClickHouse equivalent of PromQL topk by (label_0), which retains the selected series labels. GROUP BY label_0 would collapse the input to one aggregate row per group and would therefore be incorrect here. Ties require a declared deterministic comparator policy.
| # | PromQL | ClickHouse final SELECT |
|---|---|---|
| 1 | sum by (label_0) (data) |
SELECT label_0, sum(value) AS value FROM instant_samples GROUP BY label_0 |
| 2 | topk by (label_0) (3, data) |
SELECT series_id, label_0, value FROM instant_samples ORDER BY label_0, value DESC, series_id LIMIT 3 BY label_0 |
| 3 | quantile by (label_0) (q, data) |
SELECT label_0, quantileExactInclusive({q:Float64})(value) AS value FROM instant_samples GROUP BY label_0 |
| 4 | sum_over_time(data[T]) |
SELECT series_id, sum(value) AS value FROM window_samples GROUP BY series_id |
| 5 | quantile_over_time(q, data[T]) |
SELECT series_id, quantileExactInclusive({q:Float64})(value) AS value FROM window_samples GROUP BY series_id |
| 6 | rate(data[T]) |
SELECT series_id, rate_value AS value FROM per_series_counter |
| 7 | sum by (label_0) (rate(data[T])) |
SELECT label_0, sum(rate_value) AS value FROM per_series_counter GROUP BY label_0 |
| 8 | sum by (label_0) (sum_over_time(data[T])) |
SELECT label_0, sum(series_sum) AS value FROM (SELECT series_id, label_0, sum(value) AS series_sum FROM window_samples GROUP BY series_id, label_0) GROUP BY label_0 |
| 9 | topk by (label_0) (3, rate(data[T])) |
SELECT series_id, label_0, rate_value AS value FROM per_series_counter ORDER BY label_0, rate_value DESC, series_id LIMIT 3 BY label_0 |
| 10 | quantile_over_time(0.9, data[T]) / quantile_over_time(0.5, data[T]) |
SELECT series_id, quantileExactInclusive(0.9)(value) / quantileExactInclusive(0.5)(value) AS value FROM window_samples GROUP BY series_id |
For supported count_over_time, use SELECT series_id, count() AS value FROM window_samples GROUP BY series_id; for increase, use SELECT series_id, increase_value AS value FROM per_series_counter. Do not use ClickHouse's approximate quantile or approximate topK as the exact SQL baseline. Compare nonfinite values and missing series separately; the finite-only SQL above does not establish those cases.
Supported query forms and compositions
Test each form with and without label filters. Apply equivalent predicates inside the ClickHouse input CTEs. Keep the full series_id grouping for temporal operations and retain original series labels for topk.
| Form | PromQL | ClickHouse final SELECT |
|---|---|---|
| Spatial count | count by (label_0) (data) |
SELECT label_0, count() AS value FROM instant_samples GROUP BY label_0 |
| Spatial sum | sum by (label_0) (data) |
SELECT label_0, sum(value) AS value FROM instant_samples GROUP BY label_0 |
| Spatial quantile | quantile by (label_0) (q, data) |
SELECT label_0, quantileExactInclusive({q:Float64})(value) AS value FROM instant_samples GROUP BY label_0 |
| Spatial top-k | topk by (label_0) (k, data) |
SELECT series_id, label_0, value FROM instant_samples ORDER BY label_0, value DESC, series_id LIMIT {k:UInt64} BY label_0 |
| Temporal count | count_over_time(data[T]) |
SELECT series_id, count() AS value FROM window_samples GROUP BY series_id |
| Temporal sum | sum_over_time(data[T]) |
SELECT series_id, sum(value) AS value FROM window_samples GROUP BY series_id |
| Temporal quantile | quantile_over_time(q, data[T]) |
SELECT series_id, quantileExactInclusive({q:Float64})(value) AS value FROM window_samples GROUP BY series_id |
| Counter increase | increase(data[T]) |
SELECT series_id, increase_value AS value FROM per_series_counter |
| Counter rate | rate(data[T]) |
SELECT series_id, rate_value AS value FROM per_series_counter |
Also test multiple spatial aggregations, a temporal aggregation followed by spatial aggregations, and binary operations between aggregation expressions. For ClickHouse SQL, evaluate both operands at the same timestamp, preserve the PromQL grouping and label-matching rules, then join on the correct label set. Include cases for missing series, ties, and nonfinite values before claiming full composition support.
ClickHouse references: exact inclusive quantile, LIMIT BY, window functions, and argMax. The counter formula follows Prometheus 3.5.0 extrapolatedRate.
Before and after behavior
After should have these tests in MVP CI of the ASAPQuery-backend repo.
Activity