Prometheus: Fix range variables interpolation for time ranges smaller than 1 second (#42242)

* Prometheus: Fix precision for queries with < 1s timeRange

* Update pkg/tsdb/prometheus/time_series_query.go

Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>

Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
This commit is contained in:
Ivana Huckova
2021-11-29 12:41:23 +01:00
committed by GitHub
co-authored by Gábor Farkas
parent 40339fe912
commit 0b5d7067f9
2 changed files with 72 additions and 10 deletions
+15 -10
View File
@@ -177,17 +177,9 @@ func (s *Service) parseTimeSeriesQuery(queryContext *backend.QueryDataRequest, d
interval = time.Duration(int64(adjustedInterval) * intervalFactor)
}
intervalMs := int64(interval / time.Millisecond)
rangeS := query.TimeRange.To.Unix() - query.TimeRange.From.Unix()
// Interpolate variables in expr
expr := model.Expr
expr = strings.ReplaceAll(expr, varIntervalMs, strconv.FormatInt(intervalMs, 10))
expr = strings.ReplaceAll(expr, varInterval, intervalv2.FormatDuration(interval))
expr = strings.ReplaceAll(expr, varRangeMs, strconv.FormatInt(rangeS*1000, 10))
expr = strings.ReplaceAll(expr, varRangeS, strconv.FormatInt(rangeS, 10))
expr = strings.ReplaceAll(expr, varRange, strconv.FormatInt(rangeS, 10)+"s")
expr = strings.ReplaceAll(expr, varRateInterval, intervalv2.FormatDuration(calculateRateInterval(interval, dsInfo.TimeInterval, s.intervalCalculator)))
timeRange := query.TimeRange.To.Sub(query.TimeRange.From)
expr := interpolateVariables(model.Expr, interval, timeRange, s.intervalCalculator, dsInfo.TimeInterval)
rangeQuery := model.RangeQuery
if !model.InstantQuery && !model.RangeQuery {
@@ -262,6 +254,19 @@ func calculateRateInterval(interval time.Duration, scrapeInterval string, interv
return rateInterval
}
func interpolateVariables(expr string, interval time.Duration, timeRange time.Duration, intervalCalculator intervalv2.Calculator, timeInterval string) string {
rangeMs := timeRange.Milliseconds()
rangeSRounded := int64(math.Round(float64(rangeMs) / 1000.0))
expr = strings.ReplaceAll(expr, varIntervalMs, strconv.FormatInt(int64(interval/time.Millisecond), 10))
expr = strings.ReplaceAll(expr, varInterval, intervalv2.FormatDuration(interval))
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, varRateInterval, intervalv2.FormatDuration(calculateRateInterval(interval, timeInterval, intervalCalculator)))
return expr
}
func matrixToDataFrames(matrix model.Matrix, query *PrometheusQuery, frames data.Frames) data.Frames {
for _, v := range matrix {
tags := make(map[string]string, len(v.Metric))
@@ -281,6 +281,44 @@ func TestPrometheus_timeSeriesQuery_parseTimeSeriesQuery(t *testing.T) {
require.Equal(t, "rate(ALERTS{job=\"test\" [172800]})", models[0].Expr)
})
t.Run("parsing query model with $__range_s variable below 0.5s", func(t *testing.T) {
timeRange := backend.TimeRange{
From: now,
To: now.Add(40 * time.Millisecond),
}
query := queryContext(`{
"expr": "rate(ALERTS{job=\"test\" [$__range_s]})",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}`, timeRange)
dsInfo := &DatasourceInfo{}
models, err := service.parseTimeSeriesQuery(query, dsInfo)
require.NoError(t, err)
require.Equal(t, "rate(ALERTS{job=\"test\" [0]})", models[0].Expr)
})
t.Run("parsing query model with $__range_s variable between 1-0.5s", func(t *testing.T) {
timeRange := backend.TimeRange{
From: now,
To: now.Add(800 * time.Millisecond),
}
query := queryContext(`{
"expr": "rate(ALERTS{job=\"test\" [$__range_s]})",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}`, timeRange)
dsInfo := &DatasourceInfo{}
models, err := service.parseTimeSeriesQuery(query, dsInfo)
require.NoError(t, err)
require.Equal(t, "rate(ALERTS{job=\"test\" [1]})", models[0].Expr)
})
t.Run("parsing query model with $__range_ms variable", func(t *testing.T) {
timeRange := backend.TimeRange{
From: now,
@@ -300,6 +338,25 @@ func TestPrometheus_timeSeriesQuery_parseTimeSeriesQuery(t *testing.T) {
require.Equal(t, "rate(ALERTS{job=\"test\" [172800000]})", models[0].Expr)
})
t.Run("parsing query model with $__range_ms variable below 1s", func(t *testing.T) {
timeRange := backend.TimeRange{
From: now,
To: now.Add(20 * time.Millisecond),
}
query := queryContext(`{
"expr": "rate(ALERTS{job=\"test\" [$__range_ms]})",
"format": "time_series",
"intervalFactor": 1,
"refId": "A"
}`, timeRange)
dsInfo := &DatasourceInfo{}
models, err := service.parseTimeSeriesQuery(query, dsInfo)
require.NoError(t, err)
require.Equal(t, "rate(ALERTS{job=\"test\" [20]})", models[0].Expr)
})
t.Run("parsing query model with $__rate_interval variable", func(t *testing.T) {
timeRange := backend.TimeRange{
From: now,