Skip to content

Lookback vs PromQL range selector: redundant window concepts (+ /query_range readout bug) #706

Description

@milindsrivastava1997

Summary

The PromQL planning path carries two window concepts:

  • Declared lookback: time_selection.lookback, which becomes PlanningQuery.window_secs and InstantExecution.lookback_ms.
  • PromQL range selector: each leaf's [range], which becomes the per-state window_secs and MaterializationBinding.readout_lookback_ms.

Wherever a leaf has a range, the range wins for the stored window, validation, retention and most serving. Lookback only really matters for (a) rangeless states, where it silently becomes the stored window, and (b) one serving path, where using it gives wrong results (see bug below). ASAPPlanner (ca7546d) only checks lookback.is_some() and never uses the value.

Everything here was found by reading the code; nothing was run.

Bug: /query_range reads the declared lookback instead of the range

For an entry with no Logical nodes (e.g. sum(sum_over_time(a[10m])) with a 60s lookback):

  • engine.rs:661-699 calls live_serve::serve_range_steps_from_query_plan, which calls execute_query_plan_instant.
  • That computes t0 = now - entry.instant.lookback_ms (post_asap_readout.rs:111-115), so it reads 1m of panes for a 10m query.
  • Instant queries and range queries over Logical entries overwrite instant.lookback_ms with the binding's readout_lookback_ms (engine.rs:291-305), so they are unaffected.
  • QueryPlan::validate only rejects readout_lookback_ms == 0 (crates/asap_types/src/query_plan.rs:431), so nothing catches the mismatch.
  • The compiler tests that build this shape: control_plane/src/physical/compiler.rs:5424-5451, :5387-5420.

Cases

Case Example Lookback vs range What happens today
No range, exact sum by (job) (data) No [range], so only the lookback exists (1s in fixtures) The compiler won't store state for a bare scan, so Prometheus answers it. The lookback is computed but ends up unused.
No range, sketch quantile by (job) (0.5, data) No [range], so the lookback becomes the sketch's window The state gets a window from the lookback but no read-back range. Serving would reject it ("requires one explicit positive window"). The lookback quietly decides behavior here.
Multiple ranges sum(sum_over_time(a[1m])) / sum(sum_over_time(b[5m])) Two ranges, one lookback (at most one of them can match) Works. Each range gets its own state, and serving uses each state's own range. The lookback is ignored.
Single range ≠ lookback sum(sum_over_time(a[10m])), lookback 60s They conflict Compiles fine, and instant queries are correct. /query_range reads only 60s of data for a 10m query (the bug above).
Subquery avg_over_time((sum(m))[6h:]) The subquery's [6h:] range isn't picked up, so the lookback fills in The inner part isn't stored as state. How much data to keep comes from the subquery node itself, so the lookback has no effect.
offset sum_over_time(m[1m] offset 1h) The range exists but the offset shifts it The compiler rejects stored state for offset queries, so Prometheus answers it.
HTTP path Any query sent to compile-and-publish The client sends window_secs directly If the client's window candidates don't match the query's actual range: hybrid mode silently drops the state and Prometheus answers the query; non-hybrid mode fails to compile.

Only two rows depend on the lookback value itself: "No range, sketch" (it becomes the window) and "Single range ≠ lookback" (the bug). In every other row the lookback is redundant or ignored.

Checked-in fixtures (docs/examples/*.json, docs/examples/workloads/*.json) always use lookback = max range, or 1s when there is no range (generate.py:31: "lookback = scrape interval (1s)"). Mismatches only appear in Rust tests that reuse the 60s fixture.

Two concepts mixed together

  • Dashboard time span: Planner TimeSelection ("concrete event-time interval selected by a query"), ClickHouse end - start (control_plane/src/clickhouse.rs:266, 517).
  • PromQL range selector: the semantic window of a leaf.

Questions

  • Is the intended meaning of time_selection.lookback the dashboard time span or the PromQL range? If it's the time span, should it affect retention or cost at all?
  • What window should a rangeless sketch state (quantile(0.5, m)) use?
  • Does the HTTP window_secs have any caller that relies on it differing from the range?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions