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:
co-authored by
Gábor Farkas
parent
40339fe912
commit
0b5d7067f9
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user