Skip to content

Add query cost estimation and limits proposal#89

Open
roidelapluie wants to merge 1 commit into
prometheus:mainfrom
roidelapluie:roidelapluie/add-query-cost-proposal
Open

Add query cost estimation and limits proposal#89
roidelapluie wants to merge 1 commit into
prometheus:mainfrom
roidelapluie:roidelapluie/add-query-cost-proposal

Conversation

@roidelapluie

Copy link
Copy Markdown
Member

No description provided.

Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
}
```

Note: `cost=1` adds a second index lookup on top of executing the query.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why cost=1 and not cost=true? Do values of cost other than 0 and 1 have a meaning?

@roidelapluie roidelapluie Jul 2, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have cost=2 later that e.g. read chunks metadata ?

query_max_duration: 0s
```

These are enforced *during* execution against the query's actual running cost, not against the estimate: a query is rejected as soon as it loads too many series or scans too many samples, and `query_max_duration` surfaces as a query timeout. A client may lower any ceiling for a single request via `max_series`, `max_samples_scanned`, `max_query_duration`; these can only tighten, never loosen, the operator-set value. The estimate is never used to reject a query — enforcement is always on the real cost.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a request tries to override these limits to higher values than what is set on the server, is the request rejected? Or are the requested limits ignored?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"A client may lower any ceiling". The request is not rejected but you will be capped at the server's limit.

global:
query_max_series: 0 # 0 = no limit
query_max_samples_scanned: 0
query_max_duration: 0s

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this different to the existing -query.timeout flag and timeout URL parameter?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not, more a normalization of it.


Three pieces, all gated by `--enable-feature=query-cost`.

**1. Estimation (`promql.EstimateCost`).** Parse the query, walk it for every vector and matrix selector, compute the effective time window each selector reads (mirroring the engine's `getTimeRangesForSelector`/`populateSeries`), and ask storage for the series count per selector via a single querier over the union window. `SeriesTouched` is the sum across selectors — an upper bound, because a series shared between selectors is counted once per selector. `SamplesScanned` models the engine's incremental per-step reads (full range window at step 0, then only the samples that advance past the previous cutoff), scaled by a measured average per-point cost so native-histogram points are sized by bucket rather than counted as one float unit. The estimate is index-only apart from decoding at most `histogramSampleLimit` (50) points per selector to size histograms.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on how the sample count estimation would work? Would this require decoding each chunk, or would it use the sample count stored on each chunk to avoid decoding chunks?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 2 options.

In Prometheus we can take the scrape interval for estimationm,
or we can estimate based on the number of chunks and do x120.

Looking at the chunks header would be almost as I/O expensive as running the query.


Three pieces, all gated by `--enable-feature=query-cost`.

**1. Estimation (`promql.EstimateCost`).** Parse the query, walk it for every vector and matrix selector, compute the effective time window each selector reads (mirroring the engine's `getTimeRangesForSelector`/`populateSeries`), and ask storage for the series count per selector via a single querier over the union window. `SeriesTouched` is the sum across selectors — an upper bound, because a series shared between selectors is counted once per selector. `SamplesScanned` models the engine's incremental per-step reads (full range window at step 0, then only the samples that advance past the previous cutoff), scaled by a measured average per-point cost so native-histogram points are sized by bucket rather than counted as one float unit. The estimate is index-only apart from decoding at most `histogramSampleLimit` (50) points per selector to size histograms.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm understanding correctly, the goal is to roughly estimate the value of the "samples read" statistic introduced in prometheus/prometheus#18081 (rather than the "total samples" statistic that existed before that PR). Is that correct? If so, it might be worth mentioning that here.

query_max_duration: 0s
```

These are enforced *during* execution against the query's actual running cost, not against the estimate: a query is rejected as soon as it loads too many series or scans too many samples, and `query_max_duration` surfaces as a query timeout. A client may lower any ceiling for a single request via `max_series`, `max_samples_scanned`, `max_query_duration`; these can only tighten, never loosen, the operator-set value. The estimate is never used to reject a query — enforcement is always on the real cost.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it might actually make sense to enforce the limit based on the estimation, rather than allowing the query to run up until a limit is hit.

Feels wasteful to let a query that will likely be limited, run and fetch data.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say it will depend on how we can accurately estimate the limit upfront.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants