package logql import ( "testing" "time" "github.com/stretchr/testify/require" ) func Test_SplitRangeInterval(t *testing.T) { rvm, err := NewRangeMapper(2 * time.Second) require.NoError(t, err) for _, tc := range []struct { expr string expected string }{ { `bytes_over_time({app="foo"}[3s])`, `sum without( downstream> ++ downstream> )`, }, { `count_over_time({app="foo"}[5s])`, `sum without( downstream> ++ downstream> ++ downstream> )`, }, { `rate({app="foo"}[4s] offset 1m)`, `(sum without( downstream> ++ downstream> ) / 4)`, }, } { tc := tc t.Run(tc.expr, func(t *testing.T) { t.Parallel() noop, mappedExpr, err := rvm.Parse(tc.expr) require.NoError(t, err) require.Equal(t, removeWhiteSpace(tc.expected), removeWhiteSpace(mappedExpr.String())) require.Equal(t, false, noop) }) } } func Test_SplitRangeVectorMapping(t *testing.T) { rvm, err := NewRangeMapper(time.Minute) require.NoError(t, err) for _, tc := range []struct { expr string expected string }{ // Range vector aggregators { `bytes_over_time({app="foo"}[3m])`, `sum without( downstream> ++ downstream> ++ downstream> )`, }, { `count_over_time({app="foo"}[3m])`, `sum without( downstream> ++ downstream> ++ downstream> )`, }, { `sum_over_time({app="foo"} | unwrap bar [3m])`, `sum without( downstream> ++ downstream> ++ downstream> )`, }, { `max_over_time({app="foo"} | unwrap bar [3m])`, `max without( downstream> ++ downstream> ++ downstream> )`, }, { `max_over_time({app="foo"} | json | unwrap bar [3m]) by (bar)`, `max by (bar) ( downstream> ++ downstream> ++ downstream> )`, }, { `max_over_time({app="foo"} | unwrap bar [3m]) by (baz)`, `max by (baz) ( downstream> ++ downstream> ++ downstream> )`, }, { `min_over_time({app="foo"} | unwrap bar [3m])`, `min without( downstream> ++ downstream> ++ downstream> )`, }, { `min_over_time({app="foo"} | unwrap bar [3m]) by (baz)`, `min by (baz) ( downstream> ++ downstream> ++ downstream> )`, }, { `rate({app="foo"}[3m])`, `(sum without( downstream> ++ downstream> ++ downstream> ) / 180)`, }, { `bytes_rate({app="foo"}[3m])`, `(sum without( downstream> ++ downstream> ++ downstream> ) / 180)`, }, // Vector aggregator - sum { `sum(bytes_over_time({app="foo"}[3m]))`, `sum( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum(count_over_time({app="foo"}[3m]))`, `sum( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum(sum_over_time({app="foo"} | unwrap bar [3m]))`, `sum( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum(max_over_time({app="foo"} | unwrap bar [3m]))`, `sum( max without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum(max_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `sum( max by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum(min_over_time({app="foo"} | unwrap bar [3m]))`, `sum( min without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum(min_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `sum( min by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum(rate({app="foo"}[3m]))`, `sum( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, { `sum(bytes_rate({app="foo"}[3m]))`, `sum( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, // Vector aggregator - sum by { `sum by (baz) (bytes_over_time({app="foo"}[3m]))`, `sum by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum by (baz) (count_over_time({app="foo"}[3m]))`, `sum by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum by (baz) (sum_over_time({app="foo"} | unwrap bar [3m]))`, `sum by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum by (baz) (max_over_time({app="foo"} | unwrap bar [3m]))`, `sum by (baz) ( max without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum by (baz) (max_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `sum by (baz) ( max by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum by (baz) (min_over_time({app="foo"} | unwrap bar [3m]))`, `sum by (baz) ( min without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum by (baz) (min_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `sum by (baz) ( min by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `sum by (baz) (rate({app="foo"}[3m]))`, `sum by (baz) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, { `sum by (baz) (bytes_rate({app="foo"}[3m]))`, `sum by (baz) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, // Vector aggregator - count { `count(bytes_over_time({app="foo"}[3m]))`, `count( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count(count_over_time({app="foo"}[3m]))`, `count( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count(sum_over_time({app="foo"} | unwrap bar [3m]))`, `count( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count(max_over_time({app="foo"} | unwrap bar [3m]))`, `count( max without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count(max_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `count( max by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count(min_over_time({app="foo"} | unwrap bar [3m]))`, `count( min without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count(min_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `count( min by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count(rate({app="foo"}[3m]))`, `count( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, { `count(bytes_rate({app="foo"}[3m]))`, `count( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, // Vector aggregator - count by { `count by (baz) (bytes_over_time({app="foo"}[3m]))`, `count by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count by (baz) (count_over_time({app="foo"}[3m]))`, `count by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count by (baz) (sum_over_time({app="foo"} | unwrap bar [3m]))`, `count by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count by (baz) (max_over_time({app="foo"} | unwrap bar [3m]))`, `count by (baz) ( max without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count by (baz) (max_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `count by (baz) ( max by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count by (baz) (min_over_time({app="foo"} | unwrap bar [3m]))`, `count by (baz) ( min without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count by (baz) (min_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `count by (baz) ( min by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `count by (baz) (rate({app="foo"}[3m]))`, `count by (baz) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, { `count by (baz) (bytes_rate({app="foo"}[3m]))`, `count by (baz) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, // Vector aggregator - max { `max(bytes_over_time({app="foo"}[3m]))`, `max( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max(count_over_time({app="foo"}[3m]))`, `max( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max(sum_over_time({app="foo"} | unwrap bar [3m]))`, `max( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max(max_over_time({app="foo"} | unwrap bar [3m]))`, `max( max without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max(max_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `max( max by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max(min_over_time({app="foo"} | unwrap bar [3m]))`, `max( min without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max(min_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `max( min by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max(rate({app="foo"}[3m]))`, `max( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, { `max(bytes_rate({app="foo"}[3m]))`, `max( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, // Vector aggregator - max by { `max by (baz) (bytes_over_time({app="foo"}[3m]))`, `max by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max by (baz) (count_over_time({app="foo"}[3m]))`, `max by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max by (baz) (sum_over_time({app="foo"} | unwrap bar [3m]))`, `max by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max by (baz) (max_over_time({app="foo"} | unwrap bar [3m]))`, `max by (baz) ( max without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max by (baz) (max_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `max by (baz) ( max by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max by (baz) (min_over_time({app="foo"} | unwrap bar [3m]))`, `max by (baz) ( min without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max by (baz) (min_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `max by (baz) ( min by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `max by (baz) (rate({app="foo"}[3m]))`, `max by (baz) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, { `max by (baz) (bytes_rate({app="foo"}[3m]))`, `max by (baz) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, // Vector aggregator - min { `min(bytes_over_time({app="foo"}[3m]))`, `min( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min(count_over_time({app="foo"}[3m]))`, `min( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min(sum_over_time({app="foo"} | unwrap bar [3m]))`, `min( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min(max_over_time({app="foo"} | unwrap bar [3m]))`, `min( max without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min(max_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `min( max by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min(min_over_time({app="foo"} | unwrap bar [3m]))`, `min( min without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min(min_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `min( min by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min(rate({app="foo"}[3m]))`, `min( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, { `min(bytes_rate({app="foo"}[3m]))`, `min( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, // Vector aggregator - min by { `min by (baz) (bytes_over_time({app="foo"}[3m]))`, `min by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min by (baz) (count_over_time({app="foo"}[3m]))`, `min by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min by (baz) (sum_over_time({app="foo"} | unwrap bar [3m]))`, `min by (baz) ( sum without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min by (baz) (max_over_time({app="foo"} | unwrap bar [3m]))`, `min by (baz) ( max without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min by (baz) (max_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `min by (baz) ( max by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min by (baz) (min_over_time({app="foo"} | unwrap bar [3m]))`, `min by (baz) ( min without ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min by (baz) (min_over_time({app="foo"} | unwrap bar [3m]) by (baz))`, `min by (baz) ( min by (baz) ( downstream> ++ downstream> ++ downstream> ) )`, }, { `min by (baz) (rate({app="foo"}[3m]))`, `min by (baz) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, { `min by (baz) (bytes_rate({app="foo"}[3m]))`, `min by (baz) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) )`, }, // Binary operations { `bytes_over_time({app="foo"}[3m]) + count_over_time({app="foo"}[5m])`, `(sum without ( downstream> ++ downstream> ++ downstream> ) + sum without ( downstream> ++ downstream> ++ downstream> ++ downstream> ++ downstream> )) `, }, { `sum(count_over_time({app="foo"}[3m]) * count(sum_over_time({app="foo"} | unwrap bar [5m])))`, `sum( (sum without( downstream> ++ downstream> ++ downstream> ) * count ( sum without( downstream> ++ downstream> ++ downstream> ++ downstream> ++ downstream> ) )) ) `, }, { `sum by (app) (bytes_rate({app="foo"}[3m])) / sum by (app) (rate({app="foo"}[3m]))`, `( sum by (app) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) ) / sum by (app) ( (sum without ( downstream> ++ downstream> ++ downstream> ) / 180) ) )`, }, // Multi vector aggregator layer queries { `sum(max(bytes_over_time({app="foo"}[3m])))`, `sum( max( sum without( downstream> ++ downstream> ++ downstream> ) ) ) `, }, // Non-splittable vector aggregators - should go deeper in the AST { `topk(2, count_over_time({app="foo"}[3m]))`, `topk(2, sum without( downstream> ++ downstream> ++ downstream> ) )`, }, } { tc := tc t.Run(tc.expr, func(t *testing.T) { t.Parallel() noop, mappedExpr, err := rvm.Parse(tc.expr) require.NoError(t, err) require.False(t, noop) require.Equal(t, removeWhiteSpace(tc.expected), removeWhiteSpace(mappedExpr.String())) }) } } func Test_SplitRangeVectorMapping_Noop(t *testing.T) { rvm, err := NewRangeMapper(time.Minute) require.NoError(t, err) for _, tc := range []struct { expr string expected string }{ // Non-splittable range vector aggregators { `quantile_over_time(0.95, {app="foo"} | unwrap bar[3m])`, `quantile_over_time(0.95, {app="foo"} | unwrap bar[3m])`, }, { `sum(avg_over_time({app="foo"} | unwrap bar[3m]))`, `sum(avg_over_time({app="foo"} | unwrap bar[3m]))`, }, // should be noop if range interval is slower or equal to split interval (1m) { `bytes_over_time({app="foo"}[1m])`, `bytes_over_time({app="foo"}[1m])`, }, // should be noop if inner range aggregation includes a stage for label extraction such as `| json` or `| logfmt` // because otherwise the downstream queries would result in too many series { `sum(min_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `sum(min_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `sum(max_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `sum(max_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `sum(sum_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `sum(sum_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `min(min_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `min(min_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `min(max_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `min(max_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `min(sum_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `min(sum_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `max(min_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `max(min_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `max(max_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `max(max_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `max(sum_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, `max(sum_over_time({app="foo"} | logfmt | unwrap bar [3m]))`, }, { `max_over_time({app="foo"} | json | unwrap bar [3m])`, `max_over_time({app="foo"} | json | unwrap bar [3m])`, }, } { tc := tc t.Run(tc.expr, func(t *testing.T) { t.Parallel() noop, mappedExpr, err := rvm.Parse(tc.expr) require.NoError(t, err) require.True(t, noop) require.Equal(t, removeWhiteSpace(tc.expected), removeWhiteSpace(mappedExpr.String())) }) } } func Test_FailQuery(t *testing.T) { rvm, err := NewRangeMapper(2 * time.Minute) require.NoError(t, err) _, _, err = rvm.Parse(`{app="foo"} |= "err"`) require.Error(t, err) }