diff --git a/docs/sources/dashboards/variables/add-template-variables/index.md b/docs/sources/dashboards/variables/add-template-variables/index.md index 234dce7cf91..f239b57bedc 100644 --- a/docs/sources/dashboards/variables/add-template-variables/index.md +++ b/docs/sources/dashboards/variables/add-template-variables/index.md @@ -325,6 +325,10 @@ Currently only supported for Prometheus and Loki data sources. This variable rep Currently only supported for Prometheus data sources. The `$__rate_interval` variable is meant to be used in the rate function. Refer to [Prometheus query variables]({{< relref "../../../datasources/prometheus/template-variables#use-__rate_interval" >}}) for details. +### $\_\_rate_interval_ms + +This variable is the `$__rate_interval` variable in milliseconds, not a time-interval-formatted string. For example, if the `$__rate_interval` is `20m` then the `$__rate_interval_ms` is `1200000`. + ### $timeFilter or $\_\_timeFilter The `$timeFilter` variable returns the currently selected time range as an expression. For example, the time range interval `Last 7 days` expression is `time > now() - 7d`. diff --git a/pkg/tsdb/prometheus/models/query.go b/pkg/tsdb/prometheus/models/query.go index 384648bc837..91c7caf60bc 100644 --- a/pkg/tsdb/prometheus/models/query.go +++ b/pkg/tsdb/prometheus/models/query.go @@ -14,23 +14,25 @@ import ( // Internal interval and range variables const ( - varInterval = "$__interval" - varIntervalMs = "$__interval_ms" - varRange = "$__range" - varRangeS = "$__range_s" - varRangeMs = "$__range_ms" - varRateInterval = "$__rate_interval" + varInterval = "$__interval" + varIntervalMs = "$__interval_ms" + varRange = "$__range" + varRangeS = "$__range_s" + varRangeMs = "$__range_ms" + varRateInterval = "$__rate_interval" + varRateIntervalMs = "$__rate_interval_ms" ) // Internal interval and range variables with {} syntax // Repetitive code, we should have functionality to unify these const ( - varIntervalAlt = "${__interval}" - varIntervalMsAlt = "${__interval_ms}" - varRangeAlt = "${__range}" - varRangeSAlt = "${__range_s}" - varRangeMsAlt = "${__range_ms}" - varRateIntervalAlt = "${__rate_interval}" + varIntervalAlt = "${__interval}" + varIntervalMsAlt = "${__interval_ms}" + varRangeAlt = "${__range}" + varRangeSAlt = "${__range_s}" + varRangeMsAlt = "${__range_ms}" + varRateIntervalAlt = "${__rate_interval}" + varRateIntervalMsAlt = "${__rate_interval_ms}" ) type TimeSeriesQueryType string @@ -227,6 +229,7 @@ func interpolateVariables(expr, queryInterval string, interval time.Duration, expr = strings.ReplaceAll(expr, varRangeMs, strconv.FormatInt(rangeMs, 10)) expr = strings.ReplaceAll(expr, varRangeS, strconv.FormatInt(rangeSRounded, 10)) expr = strings.ReplaceAll(expr, varRange, strconv.FormatInt(rangeSRounded, 10)+"s") + expr = strings.ReplaceAll(expr, varRateIntervalMs, strconv.FormatInt(int64(rateInterval/time.Millisecond), 10)) expr = strings.ReplaceAll(expr, varRateInterval, rateInterval.String()) // Repetitive code, we should have functionality to unify these @@ -235,16 +238,17 @@ func interpolateVariables(expr, queryInterval string, interval time.Duration, expr = strings.ReplaceAll(expr, varRangeMsAlt, strconv.FormatInt(rangeMs, 10)) expr = strings.ReplaceAll(expr, varRangeSAlt, strconv.FormatInt(rangeSRounded, 10)) expr = strings.ReplaceAll(expr, varRangeAlt, strconv.FormatInt(rangeSRounded, 10)+"s") + expr = strings.ReplaceAll(expr, varRateIntervalMsAlt, strconv.FormatInt(int64(rateInterval/time.Millisecond), 10)) expr = strings.ReplaceAll(expr, varRateIntervalAlt, rateInterval.String()) return expr } func isVariableInterval(interval string) bool { - if interval == varInterval || interval == varIntervalMs || interval == varRateInterval { + if interval == varInterval || interval == varIntervalMs || interval == varRateInterval || interval == varRateIntervalMs { return true } // Repetitive code, we should have functionality to unify these - if interval == varIntervalAlt || interval == varIntervalMsAlt || interval == varRateIntervalAlt { + if interval == varIntervalAlt || interval == varIntervalMsAlt || interval == varRateIntervalAlt || interval == varRateIntervalMsAlt { return true } return false diff --git a/pkg/tsdb/prometheus/models/query_test.go b/pkg/tsdb/prometheus/models/query_test.go index dd30c5f0b24..4322ed802a1 100644 --- a/pkg/tsdb/prometheus/models/query_test.go +++ b/pkg/tsdb/prometheus/models/query_test.go @@ -385,6 +385,60 @@ func TestParse(t *testing.T) { require.Equal(t, 1*time.Minute, res.Step) }) + t.Run("parsing query model with $__rate_interval_ms variable", func(t *testing.T) { + timeRange := backend.TimeRange{ + From: now, + To: now.Add(48 * time.Hour), + } + + q := queryContext(`{ + "expr": "rate(ALERTS{job=\"test\" [$__rate_interval_ms]})", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + }`, timeRange) + + res, err := models.Parse(q, "15s", intervalCalculator, false) + require.NoError(t, err) + require.Equal(t, "rate(ALERTS{job=\"test\" [135000]})", res.Expr) + }) + + t.Run("parsing query model with $__rate_interval_ms and $__rate_interval variable", func(t *testing.T) { + timeRange := backend.TimeRange{ + From: now, + To: now.Add(48 * time.Hour), + } + + q := queryContext(`{ + "expr": "rate(ALERTS{job=\"test\" [$__rate_interval_ms]}) + rate(ALERTS{job=\"test\" [$__rate_interval]})", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + }`, timeRange) + + res, err := models.Parse(q, "15s", intervalCalculator, false) + require.NoError(t, err) + require.Equal(t, "rate(ALERTS{job=\"test\" [135000]}) + rate(ALERTS{job=\"test\" [2m15s]})", res.Expr) + }) + + t.Run("parsing query model with ${__rate_interval_ms} and ${__rate_interval} variable", func(t *testing.T) { + timeRange := backend.TimeRange{ + From: now, + To: now.Add(48 * time.Hour), + } + + q := queryContext(`{ + "expr": "rate(ALERTS{job=\"test\" [${__rate_interval_ms}]}) + rate(ALERTS{job=\"test\" [${__rate_interval}]})", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + }`, timeRange) + + res, err := models.Parse(q, "15s", intervalCalculator, false) + require.NoError(t, err) + require.Equal(t, "rate(ALERTS{job=\"test\" [135000]}) + rate(ALERTS{job=\"test\" [2m15s]})", res.Expr) + }) + t.Run("parsing query model of range query", func(t *testing.T) { timeRange := backend.TimeRange{ From: now,