From b25ffe2b1544e2ad0ee0c43c693ddb1eb24fc5cc Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Wed, 7 Feb 2024 21:32:57 +0100 Subject: [PATCH] Prometheus: Remove all intervalv2 imports and use gtime functions wherever it's possible (#82064) * Remove all intervalv2 imports and use gtime functions where ever it's possible * Add a comment line * use roundInterval from grafana-plugin-sdk-go --- pkg/tsdb/prometheus/intervalv2/intervalv2.go | 74 +++++++++++++++++++ .../prometheus/intervalv2/intervalv2_test.go | 63 ++++++++++++++++ pkg/tsdb/prometheus/models/query.go | 13 ++-- pkg/tsdb/prometheus/models/query_test.go | 2 +- pkg/tsdb/prometheus/querydata/request.go | 2 +- 5 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 pkg/tsdb/prometheus/intervalv2/intervalv2.go create mode 100644 pkg/tsdb/prometheus/intervalv2/intervalv2_test.go diff --git a/pkg/tsdb/prometheus/intervalv2/intervalv2.go b/pkg/tsdb/prometheus/intervalv2/intervalv2.go new file mode 100644 index 00000000000..cf1d943cbd7 --- /dev/null +++ b/pkg/tsdb/prometheus/intervalv2/intervalv2.go @@ -0,0 +1,74 @@ +// Package intervalv2 partially copied from https://github.com/grafana/grafana/blob/main/pkg/tsdb/intervalv2/intervalv2.go +package intervalv2 + +import ( + "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" +) + +var ( + DefaultRes int64 = 1500 + defaultMinInterval = time.Millisecond * 1 +) + +type Interval struct { + Text string + Value time.Duration +} + +type intervalCalculator struct { + minInterval time.Duration +} + +type Calculator interface { + Calculate(timerange backend.TimeRange, minInterval time.Duration, maxDataPoints int64) Interval + CalculateSafeInterval(timerange backend.TimeRange, resolution int64) Interval +} + +type CalculatorOptions struct { + MinInterval time.Duration +} + +func NewCalculator(opts ...CalculatorOptions) *intervalCalculator { + calc := &intervalCalculator{} + + for _, o := range opts { + if o.MinInterval == 0 { + calc.minInterval = defaultMinInterval + } else { + calc.minInterval = o.MinInterval + } + } + + return calc +} + +func (ic *intervalCalculator) Calculate(timerange backend.TimeRange, minInterval time.Duration, maxDataPoints int64) Interval { + to := timerange.To.UnixNano() + from := timerange.From.UnixNano() + resolution := maxDataPoints + if resolution == 0 { + resolution = DefaultRes + } + + calculatedInterval := time.Duration((to - from) / resolution) + + if calculatedInterval < minInterval { + return Interval{Text: gtime.FormatInterval(minInterval), Value: minInterval} + } + + rounded := gtime.RoundInterval(calculatedInterval) + + return Interval{Text: gtime.FormatInterval(rounded), Value: rounded} +} + +func (ic *intervalCalculator) CalculateSafeInterval(timerange backend.TimeRange, safeRes int64) Interval { + to := timerange.To.UnixNano() + from := timerange.From.UnixNano() + safeInterval := time.Duration((to - from) / safeRes) + + rounded := gtime.RoundInterval(safeInterval) + return Interval{Text: gtime.FormatInterval(rounded), Value: rounded} +} diff --git a/pkg/tsdb/prometheus/intervalv2/intervalv2_test.go b/pkg/tsdb/prometheus/intervalv2/intervalv2_test.go new file mode 100644 index 00000000000..8e604f168f1 --- /dev/null +++ b/pkg/tsdb/prometheus/intervalv2/intervalv2_test.go @@ -0,0 +1,63 @@ +package intervalv2 + +import ( + "testing" + "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/stretchr/testify/assert" +) + +func TestIntervalCalculator_Calculate(t *testing.T) { + calculator := NewCalculator(CalculatorOptions{}) + + timeNow := time.Now() + + testCases := []struct { + name string + timeRange backend.TimeRange + resolution int64 + expected string + }{ + {"from 5m to now and default resolution", backend.TimeRange{From: timeNow, To: timeNow.Add(5 * time.Minute)}, 0, "200ms"}, + {"from 5m to now and 500 resolution", backend.TimeRange{From: timeNow, To: timeNow.Add(5 * time.Minute)}, 500, "500ms"}, + {"from 15m to now and default resolution", backend.TimeRange{From: timeNow, To: timeNow.Add(15 * time.Minute)}, 0, "500ms"}, + {"from 15m to now and 100 resolution", backend.TimeRange{From: timeNow, To: timeNow.Add(15 * time.Minute)}, 100, "10s"}, + {"from 30m to now and default resolution", backend.TimeRange{From: timeNow, To: timeNow.Add(30 * time.Minute)}, 0, "1s"}, + {"from 30m to now and 3000 resolution", backend.TimeRange{From: timeNow, To: timeNow.Add(30 * time.Minute)}, 3000, "500ms"}, + {"from 1h to now and default resolution", backend.TimeRange{From: timeNow, To: timeNow.Add(time.Hour)}, 0, "2s"}, + {"from 1h to now and 1000 resolution", backend.TimeRange{From: timeNow, To: timeNow.Add(time.Hour)}, 1000, "5s"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + interval := calculator.Calculate(tc.timeRange, time.Millisecond*1, tc.resolution) + assert.Equal(t, tc.expected, interval.Text) + }) + } +} + +func TestIntervalCalculator_CalculateSafeInterval(t *testing.T) { + calculator := NewCalculator(CalculatorOptions{}) + + timeNow := time.Now() + + testCases := []struct { + name string + timeRange backend.TimeRange + safeResolution int64 + expected string + }{ + {"from 5m to now", backend.TimeRange{From: timeNow, To: timeNow.Add(5 * time.Minute)}, 11000, "20ms"}, + {"from 15m to now", backend.TimeRange{From: timeNow, To: timeNow.Add(15 * time.Minute)}, 11000, "100ms"}, + {"from 30m to now", backend.TimeRange{From: timeNow, To: timeNow.Add(30 * time.Minute)}, 11000, "200ms"}, + {"from 24h to now", backend.TimeRange{From: timeNow, To: timeNow.Add(1440 * time.Minute)}, 11000, "10s"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + interval := calculator.CalculateSafeInterval(tc.timeRange, tc.safeResolution) + assert.Equal(t, tc.expected, interval.Text) + }) + } +} diff --git a/pkg/tsdb/prometheus/models/query.go b/pkg/tsdb/prometheus/models/query.go index da5ef0f9b1a..6c155810fb7 100644 --- a/pkg/tsdb/prometheus/models/query.go +++ b/pkg/tsdb/prometheus/models/query.go @@ -9,10 +9,11 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" - "github.com/grafana/grafana/pkg/tsdb/intervalv2" + "github.com/grafana/grafana/pkg/tsdb/prometheus/intervalv2" "github.com/grafana/grafana/pkg/tsdb/prometheus/kinds/dataquery" ) @@ -194,7 +195,7 @@ func calculatePrometheusInterval( queryInterval = "" } - minInterval, err := intervalv2.GetIntervalFrom(dsScrapeInterval, queryInterval, intervalMs, 15*time.Second) + minInterval, err := gtime.GetIntervalFrom(dsScrapeInterval, queryInterval, intervalMs, 15*time.Second) if err != nil { return time.Duration(0), err } @@ -233,7 +234,7 @@ func calculateRateInterval( scrape = "15s" } - scrapeIntervalDuration, err := intervalv2.ParseIntervalStringToTimeDuration(scrape) + scrapeIntervalDuration, err := gtime.ParseIntervalStringToTimeDuration(scrape) if err != nil { return time.Duration(0) } @@ -274,7 +275,7 @@ func interpolateVariables( } expr = strings.ReplaceAll(expr, varIntervalMs, strconv.FormatInt(int64(calculatedStep/time.Millisecond), 10)) - expr = strings.ReplaceAll(expr, varInterval, intervalv2.FormatDuration(calculatedStep)) + expr = strings.ReplaceAll(expr, varInterval, gtime.FormatInterval(calculatedStep)) 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") @@ -283,7 +284,7 @@ func interpolateVariables( // Repetitive code, we should have functionality to unify these expr = strings.ReplaceAll(expr, varIntervalMsAlt, strconv.FormatInt(int64(calculatedStep/time.Millisecond), 10)) - expr = strings.ReplaceAll(expr, varIntervalAlt, intervalv2.FormatDuration(calculatedStep)) + expr = strings.ReplaceAll(expr, varIntervalAlt, gtime.FormatInterval(calculatedStep)) 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") @@ -303,7 +304,7 @@ func isVariableInterval(interval string) bool { return false } -// This function aligns query range to step and handles the time offset. +// AlignTimeRange aligns query range to step and handles the time offset. // It rounds start and end down to a multiple of step. // Prometheus caching is dependent on the range being aligned with the step. // Rounding to the step can significantly change the start and end of the range for larger steps, i.e. a week. diff --git a/pkg/tsdb/prometheus/models/query_test.go b/pkg/tsdb/prometheus/models/query_test.go index 7f806ee9a9d..2a9ffab1077 100644 --- a/pkg/tsdb/prometheus/models/query_test.go +++ b/pkg/tsdb/prometheus/models/query_test.go @@ -9,7 +9,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/stretchr/testify/require" - "github.com/grafana/grafana/pkg/tsdb/intervalv2" + "github.com/grafana/grafana/pkg/tsdb/prometheus/intervalv2" "github.com/grafana/grafana/pkg/tsdb/prometheus/models" ) diff --git a/pkg/tsdb/prometheus/querydata/request.go b/pkg/tsdb/prometheus/querydata/request.go index 41106ba85b3..c80952b1a05 100644 --- a/pkg/tsdb/prometheus/querydata/request.go +++ b/pkg/tsdb/prometheus/querydata/request.go @@ -16,8 +16,8 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing" - "github.com/grafana/grafana/pkg/tsdb/intervalv2" "github.com/grafana/grafana/pkg/tsdb/prometheus/client" + "github.com/grafana/grafana/pkg/tsdb/prometheus/intervalv2" "github.com/grafana/grafana/pkg/tsdb/prometheus/models" "github.com/grafana/grafana/pkg/tsdb/prometheus/querydata/exemplar" "github.com/grafana/grafana/pkg/tsdb/prometheus/utils"