diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 6795c6bb4c..7ad72270e7 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -261,8 +261,7 @@ func (c *flagConfig) setFeatureListOptions(logger *slog.Logger) error { c.parserOpts.EnableExperimentalFunctions = true logger.Info("Experimental PromQL functions enabled.") case "promql-duration-expr": - c.parserOpts.ExperimentalDurationExpr = true - logger.Info("Experimental duration expression parsing enabled.") + logger.Warn("This option for --enable-feature is now permanently enabled and therefore a no-op.", "option", o) case "native-histograms": logger.Warn("This option for --enable-feature is a no-op. To scrape native histograms, set the scrape_native_histograms scrape config setting to true.", "option", o) case "ooo-native-histograms": @@ -391,6 +390,9 @@ func main() { FeatureRegistry: features.DefaultRegistry, }, promslogConfig: promslog.Config{}, + // Duration expressions are enabled by default; the promql-duration-expr + // feature flag is now a no-op. + parserOpts: parser.Options{ExperimentalDurationExpr: true}, scrape: scrape.Options{ FeatureRegistry: features.DefaultRegistry, }, @@ -636,7 +638,7 @@ func main() { a.Flag("scrape.discovery-reload-interval", "Interval used by scrape manager to throttle target groups updates."). Hidden().Default("5s").SetValue(&cfg.scrape.DiscoveryReloadInterval) - a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-duration-expr, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, search-api, st-storage, st-synthesis, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details."). + a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, search-api, st-storage, st-synthesis, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details."). StringsVar(&cfg.featureList) a.Flag("agent", "Run Prometheus in 'Agent mode'.").BoolVar(&agentMode) diff --git a/cmd/prometheus/testdata/features.json b/cmd/prometheus/testdata/features.json index 1cfce7709c..c49a5f43c1 100644 --- a/cmd/prometheus/testdata/features.json +++ b/cmd/prometheus/testdata/features.json @@ -32,7 +32,7 @@ "bool": true, "by": true, "delayed_name_removal": false, - "duration_expr": false, + "duration_expr": true, "fill": false, "fill_left": false, "fill_right": false, diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go index a6dd4e0c05..385e8077d7 100644 --- a/cmd/promtool/main.go +++ b/cmd/promtool/main.go @@ -64,8 +64,10 @@ import ( var ( promqlEnableDelayedNameRemoval = false - promtoolParserOpts parser.Options - logger = promslog.New(&promslog.Config{}) + // Duration expressions are enabled by default; the promql-duration-expr + // feature flag is now a no-op. + promtoolParserOpts = parser.Options{ExperimentalDurationExpr: true} + logger = promslog.New(&promslog.Config{}) ) func init() { @@ -320,7 +322,7 @@ func main() { promQLLabelsDeleteQuery := promQLLabelsDeleteCmd.Arg("query", "PromQL query.").Required().String() promQLLabelsDeleteName := promQLLabelsDeleteCmd.Arg("name", "Name of the label to delete.").Required().String() - featureList := app.Flag("enable-feature", "Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-duration-expr, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details").Default("").Strings() + featureList := app.Flag("enable-feature", "Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details").Default("").Strings() documentationCmd := app.Command("write-documentation", "Generate command line documentation. Internal use.").Hidden() @@ -358,7 +360,7 @@ func main() { case "promql-delayed-name-removal": promqlEnableDelayedNameRemoval = true case "promql-duration-expr": - promtoolParserOpts.ExperimentalDurationExpr = true + // This feature is now permanently enabled and therefore a no-op. case "promql-extended-range-selectors": promtoolParserOpts.EnableExtendedRangeSelectors = true case "": diff --git a/cmd/promtool/main_test.go b/cmd/promtool/main_test.go index e42ca69441..7a15cf1c0b 100644 --- a/cmd/promtool/main_test.go +++ b/cmd/promtool/main_test.go @@ -702,7 +702,7 @@ func TestCheckRulesWithFeatureFlag(t *testing.T) { // As opposed to TestCheckRules calling CheckRules directly we run promtool // so the feature flag parsing can be tested. - args := []string{"-test.main", "--enable-feature=promql-experimental-functions", "--enable-feature=promql-duration-expr", "--enable-feature=promql-extended-range-selectors", "check", "rules", "testdata/features.yml"} + args := []string{"-test.main", "--enable-feature=promql-experimental-functions", "--enable-feature=promql-extended-range-selectors", "check", "rules", "testdata/features.yml"} tool := exec.Command(promtoolPath, args...) err := tool.Run() require.NoError(t, err) diff --git a/docs/command-line/prometheus.md b/docs/command-line/prometheus.md index 5ec738e768..a9f6d033ba 100644 --- a/docs/command-line/prometheus.md +++ b/docs/command-line/prometheus.md @@ -63,7 +63,7 @@ The Prometheus monitoring server | --query.timeout | Maximum time a query may take before being aborted. Use with server mode only. | `2m` | | --query.max-concurrency | Maximum number of queries executed concurrently. Use with server mode only. | `20` | | --query.max-samples | Maximum number of samples a single query can load into memory. Note that queries will fail if they try to load more samples than this into memory, so this also limits the number of samples a query can return. Use with server mode only. | `50000000` | -| --enable-feature ... | Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-duration-expr, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, search-api, st-storage, st-synthesis, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | | +| --enable-feature ... | Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, search-api, st-storage, st-synthesis, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | | | --agent | Run Prometheus in 'Agent mode'. | | | --log.level | Only log messages with the given severity or above. One of: [debug, info, warn, error] | `info` | | --log.format | Output format of log messages. One of: [logfmt, json] | `logfmt` | diff --git a/docs/command-line/promtool.md b/docs/command-line/promtool.md index 06c6f87874..dc9e3074ce 100644 --- a/docs/command-line/promtool.md +++ b/docs/command-line/promtool.md @@ -12,7 +12,7 @@ Tooling for the Prometheus monitoring system. | -h, --help | Show context-sensitive help (also try --help-long and --help-man). | | --version | Show application version. | | --experimental | Enable experimental commands. | -| --enable-feature ... | Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-duration-expr, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details | +| --enable-feature ... | Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details | diff --git a/docs/feature_flags.md b/docs/feature_flags.md index 8b500dba50..cd83ab91d6 100644 --- a/docs/feature_flags.md +++ b/docs/feature_flags.md @@ -214,66 +214,6 @@ state is mutex guarded. Cumulative-only OTLP requests are not affected. [d2c]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/deltatocumulativeprocessor -## PromQL arithmetic expressions in time durations - -`--enable-feature=promql-duration-expr` - -With this flag, arithmetic expressions can be used in time durations in range queries and offset durations. - -In range queries: -``` -rate(http_requests_total[5m * 2]) # 10 minute range -rate(http_requests_total[(5+2) * 1m]) # 7 minute range -``` - -In offset durations: -``` -http_requests_total offset (1h / 2) # 30 minute offset -http_requests_total offset ((2 ^ 3) * 1m) # 8 minute offset -``` - -When using offset with duration expressions, you must wrap the expression in -parentheses. Without parentheses, only the first duration value will be used in -the offset calculation. - -`step()` can be used in duration expressions. -For a **range query**, it resolves to the step width of the range query. -For an **instant query**, it resolves to `0s`. - -`range()` can be used in duration expressions. -For a **range query**, it resolves to the full range of the query (end time - start time). -For an **instant query**, it resolves to `0s`. -This is particularly useful in combination with `@end()` to look back over the entire query range, e.g., `max_over_time(metric[range()] @ end())`. - -`min_of(, )` and `max_of(, )` select between two duration expressions. -`min_of` returns the smaller of the two, which is useful for capping a duration at a maximum value. -`max_of` returns the larger of the two, which is useful for enforcing a minimum value. -For example, `max_of(step(), 5s)` ensures the duration is never shorter than `5s`, while `min_of(range(), 1h)` caps the duration at `1h`. - -**Note**: Duration expressions are not supported in the @ timestamp operator. - -The following operators are supported: - -* `+` - addition -* `-` - subtraction -* `*` - multiplication -* `/` - division -* `%` - modulo -* `^` - exponentiation - -Examples of equivalent durations: - -* `5m * 2` is equivalent to `10m` or `600s` -* `10m - 1m` is equivalent to `9m` or `540s` -* `(5+2) * 1m` is equivalent to `7m` or `420s` -* `1h / 2` is equivalent to `30m` or `1800s` -* `4h % 3h` is equivalent to `1h` or `3600s` -* `(2 ^ 3) * 1m` is equivalent to `8m` or `480s` -* `step() + 1` is equivalent to the query step width increased by 1s. -* `max_of(step(), 5s)` is equivalent to the larger of the query step width and `5s`. -* `min_of(2 * step() + 5s, 5m)` is equivalent to the smaller of twice the query step increased by `5s` and `5m`. - - ## OTLP Native Delta Support `--enable-feature=otlp-native-delta-ingestion` diff --git a/docs/querying/basics.md b/docs/querying/basics.md index e7f1173af4..b2ce7e757e 100644 --- a/docs/querying/basics.md +++ b/docs/querying/basics.md @@ -174,6 +174,65 @@ Examples: 12h34m56s # Equivalent to 45296s and thus 45296. 54s321ms # Equivalent to 54.321. +### Duration expressions + +Arithmetic expressions can be used wherever a time duration is expected, that is +in [range vector selectors](#range-vector-selectors) and in +[offset durations](#offset-modifier). + +In range vectors: + + rate(http_requests_total[5m * 2]) # 10 minute range + rate(http_requests_total[(5+2) * 1m]) # 7 minute range + +In offset durations: + + http_requests_total offset (1h / 2) # 30 minute offset + http_requests_total offset ((2 ^ 3) * 1m) # 8 minute offset + +When using `offset` with a duration expression, you must wrap the expression in +parentheses. Without parentheses, only the first duration value is used in the +offset calculation. + +The following operators are supported, following the usual precedence rules: + +* `+` – addition +* `-` – subtraction +* `*` – multiplication +* `/` – division +* `%` – modulo +* `^` – exponentiation + +The following functions can be used inside duration expressions: + +* `step()` resolves to the step width of a [range query](api.md#range-queries), + and to `0s` for an [instant query](api.md#instant-queries). +* `range()` resolves to the full range of a range query (end time − start time), + and to `0s` for an instant query. This is particularly useful in combination + with `@ end()` to look back over the entire query range, e.g. + `max_over_time(metric[range()] @ end())`. +* `min_of(, )` returns the smaller of the two durations, + which is useful for capping a duration at a maximum value. +* `max_of(, )` returns the larger of the two durations, + which is useful for enforcing a minimum value. + +For example, `max_of(step(), 5s)` ensures the duration is never shorter than +`5s`, while `min_of(range(), 1h)` caps the duration at `1h`. + +**Note**: Duration expressions are not supported in the [`@` modifier](#modifier). + +Examples of equivalent durations: + +* `5m * 2` is equivalent to `10m` or `600s`. +* `10m - 1m` is equivalent to `9m` or `540s`. +* `(5+2) * 1m` is equivalent to `7m` or `420s`. +* `1h / 2` is equivalent to `30m` or `1800s`. +* `4h % 3h` is equivalent to `1h` or `3600s`. +* `(2 ^ 3) * 1m` is equivalent to `8m` or `480s`. +* `step() + 1` is equivalent to the query step width increased by `1s`. +* `max_of(step(), 5s)` is equivalent to the larger of the query step width and `5s`. +* `min_of(2 * step() + 5s, 5m)` is equivalent to the smaller of twice the query step increased by `5s` and `5m`. + ## Time series selectors These are the basic building-blocks that instruct PromQL what data to fetch. diff --git a/docs/querying/functions.md b/docs/querying/functions.md index 5900da4c62..4361cfe72f 100644 --- a/docs/querying/functions.md +++ b/docs/querying/functions.md @@ -979,8 +979,7 @@ These functions act on histograms in the following way: select the first sample of `m` _within_ the 1m range, where `m offset 1m` will select the most recent sample within the lookback interval _outside and prior to_ the 1m offset. This is particularly useful with `first_over_time(m[step()])` -in range queries (available when `--enable-feature=promql-duration-expr` is set) -to ensure that the sample selected is within the range step. +in range queries to ensure that the sample selected is within the range step. ## Trigonometric Functions diff --git a/web/ui/mantine-ui/src/promql/functionDocs.tsx b/web/ui/mantine-ui/src/promql/functionDocs.tsx index df47550052..a66cd6223e 100644 --- a/web/ui/mantine-ui/src/promql/functionDocs.tsx +++ b/web/ui/mantine-ui/src/promql/functionDocs.tsx @@ -589,8 +589,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -909,8 +908,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -1240,8 +1238,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -2072,8 +2069,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -2223,8 +2219,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -2349,8 +2344,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -2475,8 +2469,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -2691,8 +2684,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -2801,8 +2793,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -3315,8 +3306,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -3425,8 +3415,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -3551,8 +3540,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -3817,8 +3805,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -3927,8 +3914,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -4037,8 +4023,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

), @@ -4147,8 +4132,7 @@ const funcDocs: Record = { first sample of m within the 1m range, where m offset 1m will select the most recent sample within the lookback interval outside and prior to the 1m offset. This is particularly useful with first_over_time(m[step()]) - in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the - sample selected is within the range step. + in range queries to ensure that the sample selected is within the range step.

),