Prometheus: Fix $__rate_interval calculation (#77234)
* Remove unused param * simple unit test * rename * rename * add some comments * Update values * refactor * rename * always calculate rate interval * fix unit tests * Fix indentation * linter fix * update test * Fixing issues with the calculation * new test * fix $__interval interpolation * fix test * add comment
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/kinds/dataquery"
|
||||
)
|
||||
@@ -76,21 +77,27 @@ type Query struct {
|
||||
UtcOffsetSec int64
|
||||
}
|
||||
|
||||
func Parse(query backend.DataQuery, timeInterval string, intervalCalculator intervalv2.Calculator, fromAlert bool) (*Query, error) {
|
||||
func Parse(query backend.DataQuery, dsScrapeInterval string, intervalCalculator intervalv2.Calculator, fromAlert bool) (*Query, error) {
|
||||
model := &QueryModel{}
|
||||
if err := json.Unmarshal(query.JSON, model); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Final interval value
|
||||
interval, err := calculatePrometheusInterval(model.Interval, timeInterval, model.IntervalMs, model.IntervalFactor, query, intervalCalculator)
|
||||
// Final step value for prometheus
|
||||
calculatedMinStep, err := calculatePrometheusInterval(model.Interval, dsScrapeInterval, model.IntervalMs, model.IntervalFactor, query, intervalCalculator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Interpolate variables in expr
|
||||
timeRange := query.TimeRange.To.Sub(query.TimeRange.From)
|
||||
expr := interpolateVariables(model.Expr, model.Interval, interval, timeRange, intervalCalculator, timeInterval)
|
||||
expr := interpolateVariables(
|
||||
model.Expr,
|
||||
query.Interval,
|
||||
calculatedMinStep,
|
||||
model.Interval,
|
||||
timeRange,
|
||||
)
|
||||
var rangeQuery, instantQuery bool
|
||||
if model.Instant == nil {
|
||||
instantQuery = false
|
||||
@@ -118,7 +125,7 @@ func Parse(query backend.DataQuery, timeInterval string, intervalCalculator inte
|
||||
|
||||
return &Query{
|
||||
Expr: expr,
|
||||
Step: interval,
|
||||
Step: calculatedMinStep,
|
||||
LegendFormat: model.LegendFormat,
|
||||
Start: query.TimeRange.From,
|
||||
End: query.TimeRange.To,
|
||||
@@ -153,7 +160,7 @@ func (query *Query) TimeRange() TimeRange {
|
||||
}
|
||||
|
||||
func calculatePrometheusInterval(
|
||||
queryInterval, timeInterval string,
|
||||
queryInterval, dsScrapeInterval string,
|
||||
intervalMs, intervalFactor int64,
|
||||
query backend.DataQuery,
|
||||
intervalCalculator intervalv2.Calculator,
|
||||
@@ -167,7 +174,7 @@ func calculatePrometheusInterval(
|
||||
queryInterval = ""
|
||||
}
|
||||
|
||||
minInterval, err := intervalv2.GetIntervalFrom(timeInterval, queryInterval, intervalMs, 15*time.Second)
|
||||
minInterval, err := intervalv2.GetIntervalFrom(dsScrapeInterval, queryInterval, intervalMs, 15*time.Second)
|
||||
if err != nil {
|
||||
return time.Duration(0), err
|
||||
}
|
||||
@@ -182,7 +189,7 @@ func calculatePrometheusInterval(
|
||||
// here is where we compare for $__rate_interval or ${__rate_interval}
|
||||
if originalQueryInterval == varRateInterval || originalQueryInterval == varRateIntervalAlt {
|
||||
// Rate interval is final and is not affected by resolution
|
||||
return calculateRateInterval(adjustedInterval, timeInterval, intervalCalculator), nil
|
||||
return calculateRateInterval(adjustedInterval, dsScrapeInterval), nil
|
||||
} else {
|
||||
queryIntervalFactor := intervalFactor
|
||||
if queryIntervalFactor == 0 {
|
||||
@@ -192,12 +199,16 @@ func calculatePrometheusInterval(
|
||||
}
|
||||
}
|
||||
|
||||
// calculateRateInterval calculates the $__rate_interval value
|
||||
// queryInterval is the value calculated range / maxDataPoints on the frontend
|
||||
// queryInterval is shown on the Query Options Panel above the query editor
|
||||
// requestedMinStep is the data source scrape interval (default 15s)
|
||||
// requestedMinStep can be changed by setting "Min Step" value in Options panel below the code editor
|
||||
func calculateRateInterval(
|
||||
interval time.Duration,
|
||||
scrapeInterval string,
|
||||
intervalCalculator intervalv2.Calculator,
|
||||
queryInterval time.Duration,
|
||||
requestedMinStep string,
|
||||
) time.Duration {
|
||||
scrape := scrapeInterval
|
||||
scrape := requestedMinStep
|
||||
if scrape == "" {
|
||||
scrape = "15s"
|
||||
}
|
||||
@@ -207,25 +218,38 @@ func calculateRateInterval(
|
||||
return time.Duration(0)
|
||||
}
|
||||
|
||||
rateInterval := time.Duration(int64(math.Max(float64(interval+scrapeIntervalDuration), float64(4)*float64(scrapeIntervalDuration))))
|
||||
rateInterval := time.Duration(int64(math.Max(float64(queryInterval+scrapeIntervalDuration), float64(4)*float64(scrapeIntervalDuration))))
|
||||
return rateInterval
|
||||
}
|
||||
|
||||
func interpolateVariables(expr, queryInterval string, interval time.Duration,
|
||||
// interpolateVariables interpolates built-in variables
|
||||
// expr PromQL query
|
||||
// queryInterval Requested interval in milliseconds. This value may be overridden by MinStep in query options
|
||||
// calculatedMinStep Calculated final step value. It was calculated in calculatePrometheusInterval
|
||||
// requestedMinStep Requested minimum step value. QueryModel.interval
|
||||
// timeRange Requested time range for query
|
||||
func interpolateVariables(
|
||||
expr string,
|
||||
queryInterval time.Duration,
|
||||
calculatedMinStep time.Duration,
|
||||
requestedMinStep string,
|
||||
timeRange time.Duration,
|
||||
intervalCalculator intervalv2.Calculator, timeInterval string) string {
|
||||
) string {
|
||||
rangeMs := timeRange.Milliseconds()
|
||||
rangeSRounded := int64(math.Round(float64(rangeMs) / 1000.0))
|
||||
|
||||
var rateInterval time.Duration
|
||||
if queryInterval == varRateInterval || queryInterval == varRateIntervalAlt {
|
||||
rateInterval = interval
|
||||
if requestedMinStep == varRateInterval || requestedMinStep == varRateIntervalAlt {
|
||||
rateInterval = calculatedMinStep
|
||||
} else {
|
||||
rateInterval = calculateRateInterval(interval, timeInterval, intervalCalculator)
|
||||
if requestedMinStep == varInterval || requestedMinStep == varIntervalAlt {
|
||||
requestedMinStep = calculatedMinStep.String()
|
||||
}
|
||||
rateInterval = calculateRateInterval(queryInterval, requestedMinStep)
|
||||
}
|
||||
|
||||
expr = strings.ReplaceAll(expr, varIntervalMs, strconv.FormatInt(int64(interval/time.Millisecond), 10))
|
||||
expr = strings.ReplaceAll(expr, varInterval, intervalv2.FormatDuration(interval))
|
||||
expr = strings.ReplaceAll(expr, varIntervalMs, strconv.FormatInt(int64(queryInterval/time.Millisecond), 10))
|
||||
expr = strings.ReplaceAll(expr, varInterval, intervalv2.FormatDuration(queryInterval))
|
||||
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")
|
||||
@@ -233,8 +257,8 @@ func interpolateVariables(expr, queryInterval string, interval time.Duration,
|
||||
expr = strings.ReplaceAll(expr, varRateInterval, rateInterval.String())
|
||||
|
||||
// Repetitive code, we should have functionality to unify these
|
||||
expr = strings.ReplaceAll(expr, varIntervalMsAlt, strconv.FormatInt(int64(interval/time.Millisecond), 10))
|
||||
expr = strings.ReplaceAll(expr, varIntervalAlt, intervalv2.FormatDuration(interval))
|
||||
expr = strings.ReplaceAll(expr, varIntervalMsAlt, strconv.FormatInt(int64(queryInterval/time.Millisecond), 10))
|
||||
expr = strings.ReplaceAll(expr, varIntervalAlt, intervalv2.FormatDuration(queryInterval))
|
||||
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")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package models_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -51,7 +52,7 @@ func TestParse(t *testing.T) {
|
||||
"expr": "go_goroutines",
|
||||
"format": "time_series",
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -69,7 +70,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -87,7 +88,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 10,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -105,7 +106,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -123,7 +124,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "240s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -140,12 +141,14 @@ func TestParse(t *testing.T) {
|
||||
"expr": "rate(ALERTS{job=\"test\" [$__interval]})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"intervalMs": 60000,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [2m]})", res.Expr)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [1m]})", res.Expr)
|
||||
require.Equal(t, 120*time.Second, res.Step)
|
||||
})
|
||||
|
||||
t.Run("parsing query model with ${__interval} variable", func(t *testing.T) {
|
||||
@@ -158,12 +161,14 @@ func TestParse(t *testing.T) {
|
||||
"expr": "rate(ALERTS{job=\"test\" [${__interval}]})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"interval": "1m",
|
||||
"intervalMs": 60000,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [2m]})", res.Expr)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [1m]})", res.Expr)
|
||||
})
|
||||
|
||||
t.Run("parsing query model with $__interval_ms variable", func(t *testing.T) {
|
||||
@@ -176,12 +181,13 @@ func TestParse(t *testing.T) {
|
||||
"expr": "rate(ALERTS{job=\"test\" [$__interval_ms]})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"intervalMs": 60000,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [120000]})", res.Expr)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [60000]})", res.Expr)
|
||||
})
|
||||
|
||||
t.Run("parsing query model with $__interval_ms and $__interval variable", func(t *testing.T) {
|
||||
@@ -194,12 +200,13 @@ func TestParse(t *testing.T) {
|
||||
"expr": "rate(ALERTS{job=\"test\" [$__interval_ms]}) + rate(ALERTS{job=\"test\" [$__interval]})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"intervalMs": 60000,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [120000]}) + rate(ALERTS{job=\"test\" [2m]})", res.Expr)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [60000]}) + rate(ALERTS{job=\"test\" [1m]})", res.Expr)
|
||||
})
|
||||
|
||||
t.Run("parsing query model with ${__interval_ms} and ${__interval} variable", func(t *testing.T) {
|
||||
@@ -212,12 +219,13 @@ func TestParse(t *testing.T) {
|
||||
"expr": "rate(ALERTS{job=\"test\" [${__interval_ms}]}) + rate(ALERTS{job=\"test\" [${__interval}]})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"intervalMs": 60000,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [120000]}) + rate(ALERTS{job=\"test\" [2m]})", res.Expr)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [60000]}) + rate(ALERTS{job=\"test\" [1m]})", res.Expr)
|
||||
})
|
||||
|
||||
t.Run("parsing query model with $__range variable", func(t *testing.T) {
|
||||
@@ -231,7 +239,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -249,7 +257,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -267,7 +275,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -285,7 +293,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -303,7 +311,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -321,7 +329,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -339,7 +347,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -358,11 +366,11 @@ func TestParse(t *testing.T) {
|
||||
"intervalFactor": 1,
|
||||
"interval": "5m",
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [5m15s]})", res.Expr)
|
||||
require.Equal(t, "rate(ALERTS{job=\"test\" [20m0s]})", res.Expr)
|
||||
})
|
||||
|
||||
t.Run("parsing query model with $__rate_interval variable in expr and interval", func(t *testing.T) {
|
||||
@@ -377,7 +385,7 @@ func TestParse(t *testing.T) {
|
||||
"intervalFactor": 1,
|
||||
"interval": "$__rate_interval",
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, 1*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -396,7 +404,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, 2*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -414,7 +422,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, 2*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -432,7 +440,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, 2*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -451,7 +459,7 @@ func TestParse(t *testing.T) {
|
||||
"intervalFactor": 1,
|
||||
"refId": "A",
|
||||
"range": true
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -471,7 +479,7 @@ func TestParse(t *testing.T) {
|
||||
"refId": "A",
|
||||
"range": true,
|
||||
"instant": true
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -490,7 +498,7 @@ func TestParse(t *testing.T) {
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, timeRange)
|
||||
}`, timeRange, time.Duration(1)*time.Minute)
|
||||
|
||||
res, err := models.Parse(q, "15s", intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
@@ -498,8 +506,164 @@ func TestParse(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func queryContext(json string, timeRange backend.TimeRange) backend.DataQuery {
|
||||
func TestRateInterval(t *testing.T) {
|
||||
type args struct {
|
||||
expr string
|
||||
interval string
|
||||
intervalMs int64
|
||||
dsScrapeInterval string
|
||||
timeRange *backend.TimeRange
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *models.Query
|
||||
}{
|
||||
{
|
||||
name: "intervalMs 100s, minStep override 150s and scrape interval 30s",
|
||||
args: args{
|
||||
expr: "rate(rpc_durations_seconds_count[$__rate_interval])",
|
||||
interval: "150s",
|
||||
intervalMs: 100000,
|
||||
dsScrapeInterval: "30s",
|
||||
},
|
||||
want: &models.Query{
|
||||
Expr: "rate(rpc_durations_seconds_count[10m0s])",
|
||||
Step: time.Second * 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "intervalMs 120s, minStep override 150s and ds scrape interval 30s",
|
||||
args: args{
|
||||
expr: "rate(rpc_durations_seconds_count[$__rate_interval])",
|
||||
interval: "150s",
|
||||
intervalMs: 120000,
|
||||
dsScrapeInterval: "30s",
|
||||
},
|
||||
want: &models.Query{
|
||||
Expr: "rate(rpc_durations_seconds_count[10m0s])",
|
||||
Step: time.Second * 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "intervalMs 120s, minStep auto (interval not overridden) and ds scrape interval 30s",
|
||||
args: args{
|
||||
expr: "rate(rpc_durations_seconds_count[$__rate_interval])",
|
||||
interval: "120s",
|
||||
intervalMs: 120000,
|
||||
dsScrapeInterval: "30s",
|
||||
},
|
||||
want: &models.Query{
|
||||
Expr: "rate(rpc_durations_seconds_count[8m0s])",
|
||||
Step: time.Second * 120,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "interval and minStep are automatically calculated and ds scrape interval 30s and time range 1 hour",
|
||||
args: args{
|
||||
expr: "rate(rpc_durations_seconds_count[$__rate_interval])",
|
||||
interval: "30s",
|
||||
intervalMs: 30000,
|
||||
dsScrapeInterval: "30s",
|
||||
timeRange: &backend.TimeRange{
|
||||
From: now,
|
||||
To: now.Add(1 * time.Hour),
|
||||
},
|
||||
},
|
||||
want: &models.Query{
|
||||
Expr: "rate(rpc_durations_seconds_count[2m30s])",
|
||||
Step: time.Second * 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minStep is $__rate_interval and ds scrape interval 30s and time range 1 hour",
|
||||
args: args{
|
||||
expr: "rate(rpc_durations_seconds_count[$__rate_interval])",
|
||||
interval: "$__rate_interval",
|
||||
intervalMs: 30000,
|
||||
dsScrapeInterval: "30s",
|
||||
timeRange: &backend.TimeRange{
|
||||
From: now,
|
||||
To: now.Add(1 * time.Hour),
|
||||
},
|
||||
},
|
||||
want: &models.Query{
|
||||
Expr: "rate(rpc_durations_seconds_count[2m0s])",
|
||||
Step: time.Minute * 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minStep is $__rate_interval and ds scrape interval 30s and time range 2 days",
|
||||
args: args{
|
||||
expr: "rate(rpc_durations_seconds_count[$__rate_interval])",
|
||||
interval: "$__rate_interval",
|
||||
intervalMs: 120000,
|
||||
dsScrapeInterval: "30s",
|
||||
timeRange: &backend.TimeRange{
|
||||
From: now,
|
||||
To: now.Add(2 * 24 * time.Hour),
|
||||
},
|
||||
},
|
||||
want: &models.Query{
|
||||
Expr: "rate(rpc_durations_seconds_count[2m30s])",
|
||||
Step: time.Second * 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minStep is $__rate_interval and ds scrape interval 15s and time range 2 days",
|
||||
args: args{
|
||||
expr: "rate(rpc_durations_seconds_count[$__rate_interval])",
|
||||
interval: "$__interval",
|
||||
intervalMs: 120000,
|
||||
dsScrapeInterval: "15s",
|
||||
timeRange: &backend.TimeRange{
|
||||
From: now,
|
||||
To: now.Add(2 * 24 * time.Hour),
|
||||
},
|
||||
},
|
||||
want: &models.Query{
|
||||
Expr: "rate(rpc_durations_seconds_count[8m0s])",
|
||||
Step: time.Second * 120,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
q := mockQuery(tt.args.expr, tt.args.interval, tt.args.intervalMs, tt.args.timeRange)
|
||||
q.MaxDataPoints = 12384
|
||||
res, err := models.Parse(q, tt.args.dsScrapeInterval, intervalCalculator, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.want.Expr, res.Expr)
|
||||
require.Equal(t, tt.want.Step, res.Step)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mockQuery(expr string, interval string, intervalMs int64, timeRange *backend.TimeRange) backend.DataQuery {
|
||||
if timeRange == nil {
|
||||
timeRange = &backend.TimeRange{
|
||||
From: now,
|
||||
To: now.Add(1 * time.Hour),
|
||||
}
|
||||
}
|
||||
return backend.DataQuery{
|
||||
Interval: 2 * time.Minute,
|
||||
JSON: []byte(fmt.Sprintf(`{
|
||||
"expr": "%s",
|
||||
"format": "time_series",
|
||||
"interval": "%s",
|
||||
"intervalMs": %v,
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
}`, expr, interval, intervalMs)),
|
||||
TimeRange: *timeRange,
|
||||
RefID: "A",
|
||||
}
|
||||
}
|
||||
|
||||
func queryContext(json string, timeRange backend.TimeRange, queryInterval time.Duration) backend.DataQuery {
|
||||
return backend.DataQuery{
|
||||
Interval: queryInterval,
|
||||
JSON: []byte(json),
|
||||
TimeRange: timeRange,
|
||||
RefID: "A",
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
// "custom": {
|
||||
// "resultType": "matrix"
|
||||
// },
|
||||
// "executedQueryString": "Expr: histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[1m0s])) by (le))\nStep: 1s"
|
||||
// "executedQueryString": "Expr: histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[4s])) by (le))\nStep: 1s"
|
||||
// }
|
||||
// Name: histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[1m0s])) by (le))
|
||||
// Name: histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[4s])) by (le))
|
||||
// Dimensions: 2 Fields by 301 Rows
|
||||
// +-----------------------------------+----------------------+
|
||||
// | Name: Time | Name: Value |
|
||||
@@ -37,7 +37,7 @@
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[1m0s])) by (le))",
|
||||
"name": "histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[4s])) by (le))",
|
||||
"meta": {
|
||||
"type": "timeseries-multi",
|
||||
"typeVersion": [
|
||||
@@ -47,7 +47,7 @@
|
||||
"custom": {
|
||||
"resultType": "matrix"
|
||||
},
|
||||
"executedQueryString": "Expr: histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[1m0s])) by (le))\nStep: 1s"
|
||||
"executedQueryString": "Expr: histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[4s])) by (le))\nStep: 1s"
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
@@ -68,7 +68,7 @@
|
||||
},
|
||||
"labels": {},
|
||||
"config": {
|
||||
"displayNameFromDS": "histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[1m0s])) by (le))"
|
||||
"displayNameFromDS": "histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[4s])) by (le))"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user