VictoriaMetrics/lib/logstorage/block_test.go
Aliaksandr Valialkin 6b0da64b30
lib/logstorage: reduce memory allocations at stats and top pipes
Use chunked allocator in order to reduce memory allocations. It allocates objects from slices of up to 64Kb size.
This improves performance for `stats` and `top` pipes by up to 2x when they are applied to big number of `by (...)` groups.

Also parallelize execution of `count_uniq`, `count_uniq_hash` and `uniq_values` stats functions,
so they are executed faster on hosts with many CPU cores when applied to fields with big number
of unique values.
2024-12-23 19:45:02 +01:00

207 lines
3.9 KiB
Go

package logstorage
import (
"fmt"
"reflect"
"testing"
)
func TestBlockMustInitFromRows(t *testing.T) {
f := func(timestamps []int64, rows [][]Field, bExpected *block) {
t.Helper()
b := &block{}
b.MustInitFromRows(timestamps, rows)
if b.uncompressedSizeBytes() >= maxUncompressedBlockSize {
t.Fatalf("expecting non-full block")
}
if !reflect.DeepEqual(b, bExpected) {
t.Fatalf("unexpected block;\ngot\n%v\nwant\n%v", b, bExpected)
}
if n := b.Len(); n != len(timestamps) {
t.Fatalf("unexpected block len; got %d; want %d", n, len(timestamps))
}
b.assertValid()
}
// An empty log entries
f(nil, nil, &block{})
f([]int64{}, [][]Field{}, &block{})
// A single row
timestamps := []int64{1234}
rows := [][]Field{
{
{
Name: "msg",
Value: "foo",
},
{
Name: "level",
Value: "error",
},
},
}
bExpected := &block{
timestamps: []int64{1234},
constColumns: []Field{
{
Name: "level",
Value: "error",
},
{
Name: "msg",
Value: "foo",
},
},
}
f(timestamps, rows, bExpected)
// Multiple log entries with the same set of fields
timestamps = []int64{3, 5}
rows = [][]Field{
{
{
Name: "job",
Value: "foo",
},
{
Name: "instance",
Value: "host1",
},
},
{
{
Name: "job",
Value: "foo",
},
{
Name: "instance",
Value: "host2",
},
},
}
bExpected = &block{
timestamps: []int64{3, 5},
columns: []column{
{
name: "instance",
values: []string{"host1", "host2"},
},
},
constColumns: []Field{
{
Name: "job",
Value: "foo",
},
},
}
f(timestamps, rows, bExpected)
// Multiple log entries with distinct set of fields
timestamps = []int64{3, 5, 10}
rows = [][]Field{
{
{
Name: "msg",
Value: "foo",
},
{
Name: "b",
Value: "xyz",
},
},
{
{
Name: "b",
Value: "xyz",
},
{
Name: "a",
Value: "aaa",
},
},
{
{
Name: "b",
Value: "xyz",
},
},
}
bExpected = &block{
timestamps: []int64{3, 5, 10},
columns: []column{
{
name: "a",
values: []string{"", "aaa", ""},
},
{
name: "msg",
values: []string{"foo", "", ""},
},
},
constColumns: []Field{
{
Name: "b",
Value: "xyz",
},
},
}
f(timestamps, rows, bExpected)
}
func TestBlockMustInitFromRowsFullBlock(t *testing.T) {
const rowsCount = 2000
timestamps := make([]int64, rowsCount)
rows := make([][]Field, rowsCount)
for i := range timestamps {
fields := make([]Field, 10)
for j := range fields {
fields[j] = Field{
Name: fmt.Sprintf("field_%d", j),
Value: "very very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong value",
}
}
rows[i] = fields
}
b := getBlock()
defer putBlock(b)
b.MustInitFromRows(timestamps, rows)
b.assertValid()
if n := b.Len(); n != len(rows) {
t.Fatalf("unexpected total log entries; got %d; want %d", n, len(rows))
}
if n := b.uncompressedSizeBytes(); n < maxUncompressedBlockSize {
t.Fatalf("expecting full block with %d bytes; got %d bytes", maxUncompressedBlockSize, n)
}
}
func TestBlockMustInitFromRows_Overflow(t *testing.T) {
f := func(rowsCount int, fieldsPerRow int, expectedRowsProcessed int) {
t.Helper()
timestamps := make([]int64, rowsCount)
rows := make([][]Field, rowsCount)
for i := range timestamps {
fields := make([]Field, fieldsPerRow)
for j := range fields {
fields[j] = Field{
Name: fmt.Sprintf("field_%d_%d", i, j),
Value: "very very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong value",
}
}
rows[i] = fields
}
b := getBlock()
defer putBlock(b)
b.MustInitFromRows(timestamps, rows)
b.assertValid()
if n := b.Len(); n != expectedRowsProcessed {
t.Fatalf("unexpected total log entries; got %d; want %d", n, expectedRowsProcessed)
}
}
f(10, 300, 6)
f(10, 10, 10)
f(15, 30, 15)
f(maxColumnsPerBlock+1000, 1, maxColumnsPerBlock)
}