mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-09-13 00:42:14 +00:00
* perf(promql): use two pointers for sliding range boundaries Replace the stale cursor heuristic in RangeManipulateStream::calculate_range with monotonic left/right cursors. The old path rescanned each evaluation window (O(E x samples-per-window)) and could lose valid samples after sparse gaps or trailing empty windows. The two pointers keep strict monotonic progress, reducing boundary generation to O(N + E) while preserving (curr-range, curr] semantics, start/end shortening, and empty-window output. Controlled release benchmarks (fixed CPU, ABBA): - Public RangeManipulate wall time: ~28% faster at 1m/15s, ~66% at 5m/15s, ~96% at 1h/15s. - Warmed distributed TQL ANALYZE 1h queries: ~17-21% faster end to end; shorter windows stayed within run-order noise. Signed-off-by: discord9 <discord9@163.com> * perf(promql): specialize changes/resets with adaptive edge counting The generic range_fn macro slices, downcasts, and rescans every overlapping window for changes() and resets(). Replace the macro path for these two functions with hand-written UDF wrappers backed by a shared private edge-count kernel: direct raw-offset scans when requested edges are few, otherwise one global u64 edge prefix so each window is answered by a prefix difference. Behavior is preserved bit-for-bit, including raw null-buffer values, NaN semantics, signed zero, infinities, empty/singleton windows, independent timestamp/value offsets, arbitrary window layouts, and exact DataFusion error messages. The shared proc macro, planner, serializer, and other range functions are untouched. Controlled release benchmarks (fixed CPU, ABBA): - Dense sliding windows (k=4/20/240): 91.7-95.6% less public UDF wall time. - Low-coverage fallback (N=4096, 8 windows): 73.9-74.4% faster. - Warmed distributed TQL ANALYZE 5m/1h changes/resets: 12.1-19.7% client and 12.0-20.9% server latency improvement; controls stayed within drift. Signed-off-by: discord9 <discord9@163.com> * ci(query-regression): include PromQL range boundary case in defaults An audit of historical query-regression runs found zero range-query coverage: all 208 PromQL ANALYZE samples were bare selectors, so range evaluation could regress without CI noticing. Wire the promql_range_boundary case (introduced in #8646) into DEFAULT_CASES so label-triggered runs measure the range path. The case is cheap: a ~0.3s synthetic fixture and about a minute of query execution per base/candidate pass. Signed-off-by: discord9 <discord9@163.com> * chore(promql): address sliding range review nits Move test-only imports into their test modules and remove the unused pre-specialization changes and resets helpers. Signed-off-by: discord9 <discord9@163.com> * style(promql): apply pinned rustfmt Signed-off-by: discord9 <discord9@163.com> * test(promql): cover sparse range results Share the changes and resets test scaffolding while keeping their behavior oracles independent. Add an end-to-end sqlness regression for sparse samples, empty intermediate windows, and a valid trailing sample. Signed-off-by: discord9 <discord9@163.com> --------- Signed-off-by: discord9 <discord9@163.com>
167 lines
4.7 KiB
TOML
167 lines
4.7 KiB
TOML
# PromQL overlapping range-boundary benchmark.
|
||
#
|
||
# 128 series × 780 timestamps at 15s cadence = 99,840 rows. Data starts one
|
||
# hour before evaluation; 481 evaluations span two hours. `count_over_time` is
|
||
# boundary-sensitive, while `sum_over_time` intentionally includes kernel scan
|
||
# work and the plain selector controls unrelated path variance.
|
||
|
||
[case]
|
||
name = "promql_range_boundary"
|
||
description = "PromQL overlapping range-boundary generation at 15-second scrape and evaluation cadence"
|
||
|
||
[scenario]
|
||
kind = "direct_readable_sst"
|
||
seed = 8623
|
||
|
||
[[scenario.tables]]
|
||
database = "public"
|
||
name = "promql_range_boundary"
|
||
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 = 128, 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 = 13
|
||
rows_per_sst = 7680
|
||
row_group_size = 1920
|
||
series_count = 128
|
||
start_unix_nanos = 1704067200000000000
|
||
step_nanos = 15000000000
|
||
time_range_layout = "non_overlapping_per_sst"
|
||
series_layout = "timestamp_major"
|
||
|
||
[[scenario.queries]]
|
||
name = "plain_selector_control_2h"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') promql_range_boundary{host=~'host.*'}"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
# Four samples per full window: fixed-overhead/small-window control.
|
||
[[scenario.queries]]
|
||
name = "count_over_time_1m"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') count_over_time(promql_range_boundary{host=~'host.*'}[1m])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
# Twenty samples per full window: representative dense overlap.
|
||
[[scenario.queries]]
|
||
name = "count_over_time_5m"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') count_over_time(promql_range_boundary{host=~'host.*'}[5m])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
# 240 samples per full window: primary boundary-sensitive stress case.
|
||
[[scenario.queries]]
|
||
name = "count_over_time_1h"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') count_over_time(promql_range_boundary{host=~'host.*'}[1h])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
# Edge-scan kernel candidates: the Phase 3 implementation replaces repeated
|
||
# per-window scans with exact edge-prefix counts.
|
||
[[scenario.queries]]
|
||
name = "changes_5m"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') changes(promql_range_boundary{host=~'host.*'}[5m])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
[[scenario.queries]]
|
||
name = "changes_1h"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') changes(promql_range_boundary{host=~'host.*'}[1h])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
[[scenario.queries]]
|
||
name = "resets_5m"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') resets(promql_range_boundary{host=~'host.*'}[5m])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
[[scenario.queries]]
|
||
name = "resets_1h"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') resets(promql_range_boundary{host=~'host.*'}[1h])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
# The deterministic wave is not a true monotonic counter. This query exercises
|
||
# the regular rate path but is not a numeric counter-rate oracle.
|
||
[[scenario.queries]]
|
||
name = "rate_wave_5m"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') rate(promql_range_boundary{host=~'host.*'}[5m])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|
||
|
||
# Same wide boundaries as count, but the sum kernel scans all window values and
|
||
# intentionally dilutes the boundary-only gain.
|
||
[[scenario.queries]]
|
||
name = "sum_over_time_1h_dilution"
|
||
kind = "tql"
|
||
query = "TQL ANALYZE VERBOSE (1704070800, 1704078000, '15s') sum_over_time(promql_range_boundary{host=~'host.*'}[1h])"
|
||
warmup = 3
|
||
iterations = 9
|
||
|
||
[scenario.queries.thresholds]
|
||
max_candidate_latency_regression_pct = 20
|