influxdb/tsdb/guard_test.go

315 lines
7.9 KiB
Go

package tsdb
import (
"testing"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/influxdata/influxdb/v2/models"
"github.com/influxdata/influxql"
)
func TestGuard(t *testing.T) {
tests := []struct {
min, max int64
names []string
expr string
point string
matches bool
}{
{ // in time matching
min: 0, max: 1000,
point: "cpu value=1 100",
matches: true,
},
{ // out of time range doesn't match
min: 0, max: 10,
names: []string{"cpu"},
point: "cpu value=1 100",
matches: false,
},
{ // measurement name matches
min: 0, max: 1000,
names: []string{"cpu"},
point: "cpu value=1 100",
matches: true,
},
{ // measurement doesn't match
min: 0, max: 1000,
names: []string{"mem"},
point: "cpu value=1 100",
matches: false,
},
{ // basic expression matching
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = 'server1'",
matches: true,
},
{ // basic expression matching
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host != 'server2'",
matches: true,
},
{ // basic expression mismatch
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = 'server2'",
matches: false,
},
{ // basic expression mismatch
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host != 'server1'",
matches: false,
},
{ // parenthesis unwrap
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "(host = 'server1')",
matches: true,
},
{ // compound expression matching
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = 'server2' or host = 'server1'",
matches: true,
},
{ // compound expression mismatch
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = 'server1' and host = 'server2'",
matches: false,
},
{ // regex expression matching
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host =~ /server1/",
matches: true,
},
{ // regex expression mismatch
min: 0, max: 1000,
point: "cpu,foo=server1 value=1 100",
expr: "host =~ /server1/",
matches: false,
},
{ // regex over-approximation
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host =~ /server2/",
matches: true,
},
{ // regex over-approximation
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host !~ /server1/",
matches: true,
},
{ // key doesn't have to come first
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "'server1' = host",
matches: true,
},
{ // key doesn't have to come first
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "'server2' = host",
matches: false,
},
{ // conservative on no var refs
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "1 = 2",
matches: true,
},
{ // expr matches measurement
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "_name = 'cpu'",
matches: true,
},
{ // expr mismatches measurement
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "_name = 'mem'",
matches: false,
},
{ // expr conservative on dual var ref
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = test",
matches: true,
},
{ // expr conservative on dual var ref mismatches
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "foo = bar",
matches: false,
},
{ // expr conservative on dual var ref involving measurement
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "_name = host",
matches: true,
},
{ // expr conservative on dual var ref involving measurement
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = _name",
matches: true,
},
{ // boolean literal matches
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "true",
matches: true,
},
{ // boolean literal mismatches
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "false",
matches: false,
},
{ // reduce and
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "true and host = 'server1'",
matches: true,
},
{ // reduce and
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = 'server1' and true",
matches: true,
},
{ // reduce or
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "false or host = 'server1'",
matches: true,
},
{ // reduce or
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = 'server1' or false",
matches: true,
},
{ // short circuit and
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "false and host = 'server1'",
matches: false,
},
{ // short circuit and
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = 'server1' and false",
matches: false,
},
{ // short circuit or
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "true or host = 'server2'",
matches: true,
},
{ // short circuit or
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = 'server2' or true",
matches: true,
},
{ // conservative match weird exprs
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "'wierd'",
matches: true,
},
{ // conservative match weird exprs
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "value::field = '1'",
matches: true,
},
{ // conservative match weird exprs
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host <= 'aaa'",
matches: true,
},
{ // conservative match weird exprs
min: 0, max: 1000,
point: "cpu,host=server1 value=1 100",
expr: "host = ('server2')",
matches: true,
},
}
for i, test := range tests {
var expr influxql.Expr
if test.expr != "" {
var err error
expr, err = influxql.ParseExpr(test.expr)
if err != nil {
t.Fatal(err)
}
}
points, err := models.ParsePointsString(test.point)
if err != nil {
t.Fatal(err)
}
guard := newGuard(test.min, test.max, test.names, expr)
if guard.Matches(points) != test.matches {
t.Errorf("%d: expected matching %q with time:[%d, %d] measurements:%v expr:%q to be %t",
i, test.point, test.min, test.max, test.names, test.expr, test.matches)
cs := &spew.ConfigState{DisableMethods: true, SpewKeys: true, Indent: " "}
t.Errorf("%d: expr: %s", i, cs.Sdump(expr))
t.Errorf("%d: guard: %s", i, cs.Sdump(guard.expr))
}
}
}
func BenchmarkGuard(b *testing.B) {
tag := func(key, value string) models.Tag {
return models.Tag{Key: []byte(key), Value: []byte(value)}
}
run := func(b *testing.B, g *guard) {
run := func(b *testing.B, batch int) {
points := make([]models.Point, batch)
for i := range points {
points[i] = models.MustNewPoint("cpu", models.Tags{
tag("t0", "v0"), tag("t1", "v1"), tag("t2", "v2"),
tag("t3", "v3"), tag("t4", "v4"), tag("t5", "v5"),
tag("t6", "v6"), tag("t7", "v7"), tag("t8", "v8"),
}, models.Fields{"value": 100}, time.Unix(0, 50))
}
for i := 0; i < b.N; i++ {
if g.Matches(points) {
b.Fatal("matched")
}
}
}
b.Run("1", func(b *testing.B) { run(b, 1) })
b.Run("100", func(b *testing.B) { run(b, 100) })
b.Run("10000", func(b *testing.B) { run(b, 10000) })
}
b.Run("Time Filtered", func(b *testing.B) {
run(b, newGuard(0, 10, nil, nil))
})
b.Run("Measurement Filtered", func(b *testing.B) {
run(b, newGuard(0, 100, []string{"mem"}, nil))
})
b.Run("Tag Filtered", func(b *testing.B) {
expr, _ := influxql.ParseExpr("t4 = 'v5'")
run(b, newGuard(0, 100, []string{"cpu"}, expr))
})
}