max_over_time) to the output of instant-vector expressions (for example, rate). Subqueries let you evaluate an instant-vector expression repeatedly over a time window and produce a range vector that range functions can consume.
Why this matters: rate(...) and other instant-vector functions return a single sample per series at each evaluation time. Range-vector functions such as max_over_time(...) require a range vector (multiple samples across time). Subqueries bridge the gap.
Subqueries let you take any expression and evaluate it over a range with a given sampling step, producing a range vector that can be consumed by range-vector functions like
max_over_time.Quick motivating examples
- For a gauge metric, you can get the maximum over the last 10 minutes directly:
- For a counter metric where you want the maximum of the rate, the following is incorrect and will fail:
rate(...) is an instant vector; max_over_time(...) expects a range vector. The 10m inside rate(...) is the per-sample lookback window used to compute the rate, not the range for max_over_time.
Subquery syntax and semantics
General form:- The inner expression (for example
rate(http_requests_total[1m])) is evaluated to produce a time series. - The
[...]after that turns the evaluated expression into a range vector by specifying:<range>— how far back each subquery sample’s range should extend (e.g.5m,1h).<resolution>— the time step between subquery samples (e.g.30s,10s).
- Optionally,
offset <duration>shifts the whole subquery back in time.
rate(http_requests_total[1m])— compute a per-second rate at each evaluation point using a 1-minute lookback.[5m:30s]— for each output time of the subquery, collect the previous 5 minutes of those rate samples, sampled every 30 seconds, producing a range vector per series.
Remember: the inner duration (e.g.
1m in rate(...)) is the per-sample lookback used to compute that instant value. The subquery range (e.g. 5m) is how far back each range vector spans. These are independent parameters and serve different purposes.Subquery components — quick reference
Visualizing what a subquery returns
A subquery produces multiple sampled values per series (a range vector). For example:2mis the range window for each sampled range vector (the subquery range).10sis the resolution (the spacing between subquery samples).1minsiderate(...)is the per-sample lookback window used when computing each rate sample.
Practical example: maximum network transmit rate
Attempting this will error becauserate(...) yields an instant vector:
max_over_time can consume:
rate(node_network_transmit_bytes_total[1m])— compute per-second transmit rate with a 1-minute lookback.[5h:30s]— collect 5 hours of those rate samples, sampled every 30 seconds.max_over_time(...)— compute the maximum per-series rate across that 5-hour window.
max_over_time) evaluates the current rates sampled across the specified range. For example:
Summary and best practices
- Use range-vector functions like
max_over_timedirectly for gauge metrics (they accept range vectors). - For counters where you want to aggregate over the values produced by
rate(...)(or other instant-vector expressions), wrap the inner expression in a subquery so the outer function receives a proper range vector:- Pattern:
max_over_time(rate(metric[rate_window])[query_range:query_step])
- Pattern:
- Choose the inner window (e.g.
1minrate(...)) to match the resolution and noise tolerance you need for per-sample calculations. - Choose the subquery range and resolution (
[range:step]) to capture the retention and sampling granularity required for the outer aggregation. - Subqueries are a compact and powerful way to combine per-sample computations with range-based aggregations in PromQL.