Loki: Implement step editor (#69648)
* Loki: Implement step editor * Update to keep value * Remove console.log * Remove white space * Update public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx Co-authored-by: Matias Chomicki <matyax@gmail.com> * Import trim * Update using of step in split queries * Add tests * Add tests * Remove step interpolation --------- Co-authored-by: Matias Chomicki <matyax@gmail.com>
This commit is contained in:
co-authored by
Matias Chomicki
parent
66d2214c3b
commit
82c125d450
@@ -126,7 +126,10 @@ func parseQuery(queryContext *backend.QueryDataRequest) ([]*lokiQuery, error) {
|
||||
interval := query.Interval
|
||||
timeRange := query.TimeRange.To.Sub(query.TimeRange.From)
|
||||
|
||||
step := calculateStep(interval, timeRange, resolution)
|
||||
step, err := calculateStep(interval, timeRange, resolution, model.Step)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expr := interpolateVariables(model.Expr, interval, timeRange)
|
||||
|
||||
|
||||
+15
-6
@@ -3,6 +3,8 @@ package loki
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||
)
|
||||
|
||||
// round the duration to the nearest millisecond larger-or-equal-to the duration
|
||||
@@ -20,12 +22,19 @@ func durationMax(d1 time.Duration, d2 time.Duration) time.Duration {
|
||||
}
|
||||
}
|
||||
|
||||
func calculateStep(baseInterval time.Duration, timeRange time.Duration, resolution int64) time.Duration {
|
||||
step := time.Duration(baseInterval.Nanoseconds() * resolution)
|
||||
func calculateStep(interval time.Duration, timeRange time.Duration, resolution int64, queryStep *string) (time.Duration, error) {
|
||||
// If we don't have step from query we calculate it from interval, time range and resolution
|
||||
if queryStep == nil || *queryStep == "" {
|
||||
step := time.Duration(interval.Nanoseconds() * resolution)
|
||||
safeStep := timeRange / 11000
|
||||
chosenStep := durationMax(step, safeStep)
|
||||
return ceilMs(chosenStep), nil
|
||||
}
|
||||
|
||||
safeStep := timeRange / 11000
|
||||
step, err := intervalv2.ParseIntervalStringToTimeDuration(*queryStep)
|
||||
if err != nil {
|
||||
return step, err
|
||||
}
|
||||
|
||||
chosenStep := durationMax(step, safeStep)
|
||||
|
||||
return ceilMs(chosenStep)
|
||||
return time.Duration(step.Nanoseconds() * resolution), nil
|
||||
}
|
||||
|
||||
+96
-35
@@ -8,50 +8,111 @@ import (
|
||||
)
|
||||
|
||||
func TestLokiStep(t *testing.T) {
|
||||
t.Run("base case", func(t *testing.T) {
|
||||
require.Equal(t, time.Second*14, calculateStep(time.Second*7, time.Second, 2))
|
||||
})
|
||||
t.Run("with query step", func(t *testing.T) {
|
||||
t.Run("valid step in go duration format", func(t *testing.T) {
|
||||
queryStep := "1m"
|
||||
step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Minute*2, step)
|
||||
})
|
||||
|
||||
t.Run("step should be at least 1 millisecond", func(t *testing.T) {
|
||||
require.Equal(t, time.Millisecond*1, calculateStep(time.Microsecond*500, time.Second, 1))
|
||||
})
|
||||
t.Run("valid step as number", func(t *testing.T) {
|
||||
queryStep := "30"
|
||||
step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Minute*1, step)
|
||||
})
|
||||
|
||||
t.Run("safeInterval should happen", func(t *testing.T) {
|
||||
// safeInterval
|
||||
require.Equal(t, time.Second*3, calculateStep(time.Second*2, time.Second*33000, 1))
|
||||
})
|
||||
// calculateStep parses a duration with support for unit that Grafana uses (e.g 1d)
|
||||
t.Run("step with 1d", func(t *testing.T) {
|
||||
queryStep := "1d"
|
||||
step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Hour*48, step)
|
||||
})
|
||||
|
||||
t.Run("step should math.Ceil in milliseconds", func(t *testing.T) {
|
||||
require.Equal(t, time.Millisecond*2, calculateStep(time.Microsecond*1234, time.Second*1, 1))
|
||||
})
|
||||
// calculateStep parses a duration with support for unit that Grafana uses (e.g 1w)
|
||||
t.Run("step with 1w", func(t *testing.T) {
|
||||
queryStep := "1w"
|
||||
step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Hour*336, step)
|
||||
})
|
||||
|
||||
t.Run("step should math.Ceil in milliseconds, even if safeInterval happens", func(t *testing.T) {
|
||||
require.Equal(t, time.Millisecond*3001, calculateStep(time.Second*2, time.Second*33001, 1))
|
||||
// Returns error
|
||||
t.Run("invalid step", func(t *testing.T) {
|
||||
queryStep := "invalid"
|
||||
step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, time.Duration(0), step)
|
||||
})
|
||||
})
|
||||
t.Run("with no query step", func(t *testing.T) {
|
||||
t.Run("base case", func(t *testing.T) {
|
||||
step, err := calculateStep(time.Second*7, time.Second, 2, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*14, step)
|
||||
})
|
||||
|
||||
t.Run("resolution should happen", func(t *testing.T) {
|
||||
require.Equal(t, time.Second*5, calculateStep(time.Second*1, time.Second*100, 5))
|
||||
})
|
||||
t.Run("step should be at least 1 millisecond", func(t *testing.T) {
|
||||
step, err := calculateStep(time.Microsecond*500, time.Second, 1, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Millisecond*1, step)
|
||||
})
|
||||
|
||||
t.Run("safeInterval check should happen after resolution is used", func(t *testing.T) {
|
||||
require.Equal(t, time.Second*4, calculateStep(time.Second*2, time.Second*33000, 2))
|
||||
})
|
||||
t.Run("safeInterval should happen", func(t *testing.T) {
|
||||
// safeInterval
|
||||
step, err := calculateStep(time.Second*2, time.Second*33000, 1, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*3, step)
|
||||
})
|
||||
|
||||
t.Run("survive interval=0", func(t *testing.T) {
|
||||
// interval=0. this should never happen, but we make sure we return something sane
|
||||
// (in this case safeInterval will take care of the problem)
|
||||
require.Equal(t, time.Second*2, calculateStep(time.Second*0, time.Second*22000, 1))
|
||||
})
|
||||
t.Run("step should math.Ceil in milliseconds", func(t *testing.T) {
|
||||
step, err := calculateStep(time.Microsecond*1234, time.Second*1, 1, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Millisecond*2, step)
|
||||
})
|
||||
|
||||
t.Run("survive resolution=0", func(t *testing.T) {
|
||||
// resolution=0. this should never happen, but we make sure we return something sane
|
||||
// (in this case safeInterval will take care of the problem)
|
||||
require.Equal(t, time.Second*2, calculateStep(time.Second*1, time.Second*22000, 0))
|
||||
})
|
||||
t.Run("step should math.Ceil in milliseconds, even if safeInterval happens", func(t *testing.T) {
|
||||
step, err := calculateStep(time.Second*2, time.Second*33001, 1, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Millisecond*3001, step)
|
||||
})
|
||||
|
||||
t.Run("survive interval=0 and resolution=0", func(t *testing.T) {
|
||||
// resolution=0 and interval=0. this should never happen, but we make sure we return something sane
|
||||
// (in this case safeInterval will take care of the problem)
|
||||
require.Equal(t, time.Second*2, calculateStep(time.Second*0, time.Second*22000, 0))
|
||||
t.Run("resolution should happen", func(t *testing.T) {
|
||||
step, err := calculateStep(time.Second*1, time.Second*100, 5, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*5, step)
|
||||
})
|
||||
|
||||
t.Run("safeInterval check should happen after resolution is used", func(t *testing.T) {
|
||||
step, err := calculateStep(time.Second*2, time.Second*33000, 2, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*4, step)
|
||||
})
|
||||
|
||||
t.Run("survive interval=0", func(t *testing.T) {
|
||||
// interval=0. this should never happen, but we make sure we return something sane
|
||||
// (in this case safeInterval will take care of the problem)
|
||||
step, err := calculateStep(time.Second*0, time.Second*22000, 1, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*2, step)
|
||||
})
|
||||
|
||||
t.Run("survive resolution=0", func(t *testing.T) {
|
||||
// resolution=0. this should never happen, but we make sure we return something sane
|
||||
// (in this case safeInterval will take care of the problem)
|
||||
step, err := calculateStep(time.Second*1, time.Second*22000, 0, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*2, step)
|
||||
})
|
||||
|
||||
t.Run("survive interval=0 and resolution=0", func(t *testing.T) {
|
||||
// resolution=0 and interval=0. this should never happen, but we make sure we return something sane
|
||||
// (in this case safeInterval will take care of the problem)
|
||||
step, err := calculateStep(time.Second*0, time.Second*22000, 0, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*2, step)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user