Like Prometheus, but for logs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
loki/pkg/compute/logical_test.go

418 lines
12 KiB

package compute_test
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/grafana/loki/v3/pkg/columnar"
"github.com/grafana/loki/v3/pkg/columnar/columnartest"
"github.com/grafana/loki/v3/pkg/compute"
"github.com/grafana/loki/v3/pkg/memory"
)
func TestNot(t *testing.T) {
t.Run("invalid scalar kind", func(t *testing.T) {
var alloc memory.Allocator
res, err := compute.Not(&alloc, new(columnar.NullScalar))
require.Error(t, err, "invalid function call should result in an error")
require.Nil(t, res, "invalid function call should not result in a datum")
})
t.Run("invalid array kind", func(t *testing.T) {
var alloc memory.Allocator
res, err := compute.Not(&alloc, new(columnar.UTF8))
require.Error(t, err, "invalid function call should result in an error")
require.Nil(t, res, "invalid function call should not result in a datum")
})
t.Run("scalar", func(t *testing.T) {
tt := []struct {
name string
input *columnar.BoolScalar
expect *columnar.BoolScalar
}{
{
name: "true",
input: &columnar.BoolScalar{Value: true},
expect: &columnar.BoolScalar{Value: false},
},
{
name: "false",
input: &columnar.BoolScalar{Value: false},
expect: &columnar.BoolScalar{Value: true},
},
{
name: "null",
input: &columnar.BoolScalar{Null: true},
expect: &columnar.BoolScalar{Null: true},
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
var alloc memory.Allocator
actual, err := compute.Not(&alloc, tc.input)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, tc.expect, actual)
})
}
})
t.Run("array", func(t *testing.T) {
var alloc memory.Allocator
var (
input = columnartest.Array(t, columnar.KindBool, &alloc, true, nil, false)
expect = columnartest.Array(t, columnar.KindBool, &alloc, false, nil, true)
)
actual, err := compute.Not(&alloc, input)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, expect, actual)
})
t.Run("fully valid array", func(t *testing.T) {
var alloc memory.Allocator
var (
input = columnartest.Array(t, columnar.KindBool, &alloc, true, false, true, false)
expect = columnartest.Array(t, columnar.KindBool, &alloc, false, true, false, true)
)
actual, err := compute.Not(&alloc, input)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, expect, actual)
})
}
func TestAnd(t *testing.T) {
var alloc memory.Allocator
tt := []struct {
name string
left, right columnar.Datum
expect columnar.Datum
expectError bool
}{
{
name: "fails on non-boolean types",
left: columnartest.Scalar(t, columnar.KindInt64, 0),
right: columnartest.Scalar(t, columnar.KindInt64, 0),
expectError: true,
},
{
name: "fails on mismatch length arrays",
left: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true, false),
right: columnartest.Array(t, columnar.KindBool, &alloc, true, false),
expectError: true,
},
// (scalar, scalar) tests
{
name: "valid-scalar AND null-scalar",
left: columnartest.Scalar(t, columnar.KindBool, true),
right: columnartest.Scalar(t, columnar.KindBool, false),
expect: columnartest.Scalar(t, columnar.KindBool, false),
},
{
name: "valid-scalar AND null-scalar",
left: columnartest.Scalar(t, columnar.KindBool, true),
right: columnartest.Scalar(t, columnar.KindBool, nil),
expect: columnartest.Scalar(t, columnar.KindBool, nil),
},
{
name: "null-scalar AND valid-scalar",
left: columnartest.Scalar(t, columnar.KindBool, nil),
right: columnartest.Scalar(t, columnar.KindBool, false),
expect: columnartest.Scalar(t, columnar.KindBool, nil),
},
{
name: "null-scalar AND null-scalar",
left: columnartest.Scalar(t, columnar.KindBool, nil),
right: columnartest.Scalar(t, columnar.KindBool, nil),
expect: columnartest.Scalar(t, columnar.KindBool, nil),
},
// (scalar, array) tests
{
name: "true-scalar AND array",
left: columnartest.Scalar(t, columnar.KindBool, true),
right: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
},
{
name: "false-scalar AND array",
left: columnartest.Scalar(t, columnar.KindBool, false),
right: columnartest.Array(t, columnar.KindBool, &alloc, false, false, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, false, false, nil),
},
{
name: "null-scalar AND array",
left: columnartest.Scalar(t, columnar.KindBool, nil),
right: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, nil, nil, nil),
},
// (array, scalar) tests
{
name: "array AND true-scalar",
left: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
right: columnartest.Scalar(t, columnar.KindBool, true),
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
},
{
name: "array AND false-scalar",
left: columnartest.Array(t, columnar.KindBool, &alloc, false, false, nil),
right: columnartest.Scalar(t, columnar.KindBool, false),
expect: columnartest.Array(t, columnar.KindBool, &alloc, false, false, nil),
},
{
name: "array AND null-scalar",
left: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
right: columnartest.Scalar(t, columnar.KindBool, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, nil, nil, nil),
},
// (array, array) tests
{
name: "array AND array",
left: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true, false, nil, nil, nil),
right: columnartest.Array(t, columnar.KindBool, &alloc, false, true, true, false, true, false, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, false, false, true, false, nil, nil, nil),
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
actual, err := compute.And(&alloc, tc.left, tc.right)
if tc.expectError {
require.Error(t, err, "invalid function call should result in an error")
return
}
require.NoError(t, err, "valid function call should not result in an error")
columnartest.RequireDatumsEqual(t, tc.expect, actual)
})
}
}
func TestOr(t *testing.T) {
var alloc memory.Allocator
tt := []struct {
name string
left, right columnar.Datum
expect columnar.Datum
expectError bool
}{
{
name: "fails on non-boolean types",
left: columnartest.Scalar(t, columnar.KindInt64, 0),
right: columnartest.Scalar(t, columnar.KindInt64, 0),
expectError: true,
},
{
name: "fails on mismatch length arrays",
left: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true, false),
right: columnartest.Array(t, columnar.KindBool, &alloc, true, false),
expectError: true,
},
// (scalar, scalar) tests
{
name: "valid-scalar OR null-scalar",
left: columnartest.Scalar(t, columnar.KindBool, true),
right: columnartest.Scalar(t, columnar.KindBool, false),
expect: columnartest.Scalar(t, columnar.KindBool, true),
},
{
name: "valid-scalar OR null-scalar",
left: columnartest.Scalar(t, columnar.KindBool, true),
right: columnartest.Scalar(t, columnar.KindBool, nil),
expect: columnartest.Scalar(t, columnar.KindBool, nil),
},
{
name: "null-scalar OR valid-scalar",
left: columnartest.Scalar(t, columnar.KindBool, nil),
right: columnartest.Scalar(t, columnar.KindBool, false),
expect: columnartest.Scalar(t, columnar.KindBool, nil),
},
{
name: "null-scalar OR null-scalar",
left: columnartest.Scalar(t, columnar.KindBool, nil),
right: columnartest.Scalar(t, columnar.KindBool, nil),
expect: columnartest.Scalar(t, columnar.KindBool, nil),
},
// (scalar, array) tests
{
name: "true-scalar OR array",
left: columnartest.Scalar(t, columnar.KindBool, true),
right: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, true, nil),
},
{
name: "false-scalar OR array",
left: columnartest.Scalar(t, columnar.KindBool, false),
right: columnartest.Array(t, columnar.KindBool, &alloc, false, true, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, false, true, nil),
},
{
name: "null-scalar OR array",
left: columnartest.Scalar(t, columnar.KindBool, nil),
right: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, nil, nil, nil),
},
// (array, scalar) tests
{
name: "array OR true-scalar",
left: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
right: columnartest.Scalar(t, columnar.KindBool, true),
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, true, nil),
},
{
name: "array OR false-scalar",
left: columnartest.Array(t, columnar.KindBool, &alloc, false, false, nil),
right: columnartest.Scalar(t, columnar.KindBool, false),
expect: columnartest.Array(t, columnar.KindBool, &alloc, false, false, nil),
},
{
name: "array OR null-scalar",
left: columnartest.Array(t, columnar.KindBool, &alloc, true, false, nil),
right: columnartest.Scalar(t, columnar.KindBool, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, nil, nil, nil),
},
// (array, array) tests
{
name: "array OR array",
left: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true, false, nil, nil, nil),
right: columnartest.Array(t, columnar.KindBool, &alloc, false, true, true, false, true, false, nil),
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, true, true, false, nil, nil, nil),
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
actual, err := compute.Or(&alloc, tc.left, tc.right)
if tc.expectError {
require.Error(t, err, "invalid function call should result in an error")
return
}
require.NoError(t, err, "valid function call should not result in an error")
columnartest.RequireDatumsEqual(t, tc.expect, actual)
})
}
}
func BenchmarkNot_Array(b *testing.B) {
var alloc memory.Allocator
builder := columnar.NewBoolBuilder(&alloc)
builder.Grow(8192)
rnd := rand.New(rand.NewSource(0))
for range 8192 {
valid := rnd.Intn(2)
if valid != 1 {
builder.AppendNull()
continue
}
builder.AppendValue(rnd.Intn(2) == 1)
}
input := builder.Build()
tempAlloc := memory.NewAllocator(&alloc)
for b.Loop() {
tempAlloc.Reclaim()
_, _ = compute.Not(tempAlloc, input)
}
b.SetBytes(int64(input.Size()))
b.ReportMetric(float64(input.Len()*b.N)/b.Elapsed().Seconds(), "values/s")
}
func BenchmarkAnd_Array(b *testing.B) {
var alloc memory.Allocator
leftBuilder := columnar.NewBoolBuilder(&alloc)
leftBuilder.Grow(8192)
rightBuilder := columnar.NewBoolBuilder(&alloc)
rightBuilder.Grow(8192)
rnd := rand.New(rand.NewSource(0))
for range 8192 {
for _, builder := range []*columnar.BoolBuilder{leftBuilder, rightBuilder} {
valid := rnd.Intn(2)
if valid != 1 {
builder.AppendNull()
continue
}
builder.AppendValue(rnd.Intn(2) == 1)
}
}
var (
left = leftBuilder.Build()
right = rightBuilder.Build()
)
tempAlloc := memory.NewAllocator(&alloc)
for b.Loop() {
tempAlloc.Reclaim()
_, _ = compute.And(tempAlloc, left, right)
}
totalValues := left.Len() + right.Len()
b.SetBytes(int64(left.Size() + right.Size()))
b.ReportMetric(float64(totalValues*b.N)/b.Elapsed().Seconds(), "values/s")
}
func BenchmarkOr_Array(b *testing.B) {
var alloc memory.Allocator
leftBuilder := columnar.NewBoolBuilder(&alloc)
leftBuilder.Grow(8192)
rightBuilder := columnar.NewBoolBuilder(&alloc)
rightBuilder.Grow(8192)
rnd := rand.New(rand.NewSource(0))
for range 8192 {
for _, builder := range []*columnar.BoolBuilder{leftBuilder, rightBuilder} {
valid := rnd.Intn(2)
if valid != 1 {
builder.AppendNull()
continue
}
builder.AppendValue(rnd.Intn(2) == 1)
}
}
var (
left = leftBuilder.Build()
right = rightBuilder.Build()
)
tempAlloc := memory.NewAllocator(&alloc)
for b.Loop() {
tempAlloc.Reclaim()
_, _ = compute.Or(tempAlloc, left, right)
}
totalValues := left.Len() + right.Len()
b.SetBytes(int64(left.Size() + right.Size()))
b.ReportMetric(float64(totalValues*b.N)/b.Elapsed().Seconds(), "values/s")
}