mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
IntervalV2: Use maxDataPoints to calculate correct interval (#39036)
* Use max data points to calculate interval * Fix mockCalculator to mach Calculator * Fix incorrect merge, replace queryModel with query
This commit is contained in:
parent
b97e2da2bf
commit
f172701043
@ -6,14 +6,15 @@ import (
|
||||
|
||||
// Query represents the time series query model of the datasource
|
||||
type Query struct {
|
||||
TimeField string `json:"timeField"`
|
||||
RawQuery string `json:"query"`
|
||||
BucketAggs []*BucketAgg `json:"bucketAggs"`
|
||||
Metrics []*MetricAgg `json:"metrics"`
|
||||
Alias string `json:"alias"`
|
||||
Interval string
|
||||
IntervalMs int64
|
||||
RefID string
|
||||
TimeField string `json:"timeField"`
|
||||
RawQuery string `json:"query"`
|
||||
BucketAggs []*BucketAgg `json:"bucketAggs"`
|
||||
Metrics []*MetricAgg `json:"metrics"`
|
||||
Alias string `json:"alias"`
|
||||
Interval string
|
||||
IntervalMs int64
|
||||
RefID string
|
||||
MaxDataPoints int64
|
||||
}
|
||||
|
||||
// BucketAgg represents a bucket aggregation of the time series query model of the datasource
|
||||
|
@ -70,7 +70,7 @@ func (e *timeSeriesQuery) processQuery(q *Query, ms *es.MultiSearchRequestBuilde
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
interval := e.intervalCalculator.Calculate(e.dataQueries[0].TimeRange, minInterval)
|
||||
interval := e.intervalCalculator.Calculate(e.dataQueries[0].TimeRange, minInterval, q.MaxDataPoints)
|
||||
|
||||
b := ms.Search(interval)
|
||||
b.Size(0)
|
||||
@ -400,13 +400,14 @@ func (p *timeSeriesQueryParser) parse(tsdbQuery []backend.DataQuery) ([]*Query,
|
||||
interval := model.Get("interval").MustString("")
|
||||
|
||||
queries = append(queries, &Query{
|
||||
TimeField: timeField,
|
||||
RawQuery: rawQuery,
|
||||
BucketAggs: bucketAggs,
|
||||
Metrics: metrics,
|
||||
Alias: alias,
|
||||
Interval: interval,
|
||||
RefID: q.RefID,
|
||||
TimeField: timeField,
|
||||
RawQuery: rawQuery,
|
||||
BucketAggs: bucketAggs,
|
||||
Metrics: metrics,
|
||||
Alias: alias,
|
||||
Interval: interval,
|
||||
RefID: q.RefID,
|
||||
MaxDataPoints: q.MaxDataPoints,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ type intervalCalculator struct {
|
||||
}
|
||||
|
||||
type Calculator interface {
|
||||
Calculate(timerange backend.TimeRange, minInterval time.Duration) Interval
|
||||
Calculate(timerange backend.TimeRange, minInterval time.Duration, maxDataPoints int64) Interval
|
||||
CalculateSafeInterval(timerange backend.TimeRange, resolution int64) Interval
|
||||
}
|
||||
|
||||
@ -53,16 +53,21 @@ func (i *Interval) Milliseconds() int64 {
|
||||
return i.Value.Nanoseconds() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
func (ic *intervalCalculator) Calculate(timerange backend.TimeRange, minInterval time.Duration) Interval {
|
||||
func (ic *intervalCalculator) Calculate(timerange backend.TimeRange, minInterval time.Duration, maxDataPoints int64) Interval {
|
||||
to := timerange.To.UnixNano()
|
||||
from := timerange.From.UnixNano()
|
||||
calculatedIntrvl := time.Duration((to - from) / defaultRes)
|
||||
resolution := maxDataPoints
|
||||
if resolution == 0 {
|
||||
resolution = defaultRes
|
||||
}
|
||||
|
||||
if calculatedIntrvl < minInterval {
|
||||
calculatedInterval := time.Duration((to - from) / resolution)
|
||||
|
||||
if calculatedInterval < minInterval {
|
||||
return Interval{Text: interval.FormatDuration(minInterval), Value: minInterval}
|
||||
}
|
||||
|
||||
rounded := roundInterval(calculatedIntrvl)
|
||||
rounded := roundInterval(calculatedInterval)
|
||||
|
||||
return Interval{Text: interval.FormatDuration(rounded), Value: rounded}
|
||||
}
|
||||
|
@ -15,19 +15,24 @@ func TestIntervalCalculator_Calculate(t *testing.T) {
|
||||
timeNow := time.Now()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
timeRange backend.TimeRange
|
||||
expected string
|
||||
name string
|
||||
timeRange backend.TimeRange
|
||||
resolution int64
|
||||
expected string
|
||||
}{
|
||||
{"from 5m to now", backend.TimeRange{From: timeNow, To: timeNow.Add(5 * time.Minute)}, "200ms"},
|
||||
{"from 15m to now", backend.TimeRange{From: timeNow, To: timeNow.Add(15 * time.Minute)}, "500ms"},
|
||||
{"from 30m to now", backend.TimeRange{From: timeNow, To: timeNow.Add(30 * time.Minute)}, "1s"},
|
||||
{"from 1h to now", backend.TimeRange{From: timeNow, To: timeNow.Add(60 * time.Minute)}, "2s"},
|
||||
{"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(60 * time.Minute)}, 0, "2s"},
|
||||
{"from 1h to now and 1000 resoluion", backend.TimeRange{From: timeNow, To: timeNow.Add(60 * time.Minute)}, 1000, "5s"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
interval := calculator.Calculate(tc.timeRange, time.Millisecond*1)
|
||||
interval := calculator.Calculate(tc.timeRange, time.Millisecond*1, tc.resolution)
|
||||
assert.Equal(t, tc.expected, interval.Text)
|
||||
})
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ func (s *Service) parseQuery(dsInfo *datasourceInfo, queryContext *backend.Query
|
||||
return nil, fmt.Errorf("failed to parse Interval: %v", err)
|
||||
}
|
||||
|
||||
interval := s.intervalCalculator.Calculate(query.TimeRange, dsInterval)
|
||||
interval := s.intervalCalculator.Calculate(query.TimeRange, dsInterval, query.MaxDataPoints)
|
||||
|
||||
var resolution int64 = 1
|
||||
if model.Resolution >= 1 && model.Resolution <= 5 || model.Resolution == 10 {
|
||||
|
@ -178,7 +178,7 @@ type mockCalculator struct {
|
||||
interval intervalv2.Interval
|
||||
}
|
||||
|
||||
func (m mockCalculator) Calculate(timerange backend.TimeRange, minInterval time.Duration) intervalv2.Interval {
|
||||
func (m mockCalculator) Calculate(timerange backend.TimeRange, minInterval time.Duration, maxDataPoints int64) intervalv2.Interval {
|
||||
return m.interval
|
||||
}
|
||||
|
||||
|
@ -257,7 +257,7 @@ func (s *Service) parseQuery(queryContext *backend.QueryDataRequest, dsInfo *Dat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
calculatedInterval := s.intervalCalculator.Calculate(query.TimeRange, minInterval)
|
||||
calculatedInterval := s.intervalCalculator.Calculate(query.TimeRange, minInterval, query.MaxDataPoints)
|
||||
safeInterval := s.intervalCalculator.CalculateSafeInterval(query.TimeRange, int64(safeRes))
|
||||
|
||||
adjustedInterval := safeInterval.Value
|
||||
|
@ -377,7 +377,7 @@ var Interpolate = func(query backend.DataQuery, timeRange backend.TimeRange, tim
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
interval := sqlIntervalCalculator.Calculate(timeRange, minInterval)
|
||||
interval := sqlIntervalCalculator.Calculate(timeRange, minInterval, query.MaxDataPoints)
|
||||
|
||||
sql = strings.ReplaceAll(sql, "$__interval_ms", strconv.FormatInt(interval.Milliseconds(), 10))
|
||||
sql = strings.ReplaceAll(sql, "$__interval", interval.Text)
|
||||
|
Loading…
Reference in New Issue
Block a user