VictoriaMetrics/lib/mergeset/encoding_test.go
Aliaksandr Valialkin 636c55b526 lib/mergeset: reduce memory usage for inmemoryBlock by using more compact items representation
This also should reduce CPU time spent by GC, since inmemoryBlock.items don't have pointers now,
so GC doesn't need visiting them.
2021-02-21 22:06:47 +02:00

184 lines
4.5 KiB
Go

package mergeset
import (
"math/rand"
"reflect"
"sort"
"sync"
"testing"
"testing/quick"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
)
func TestInmemoryBlockAdd(t *testing.T) {
var ib inmemoryBlock
for i := 0; i < 30; i++ {
var items []string
totalLen := 0
ib.Reset()
// Fill ib.
for j := 0; j < i*100+1; j++ {
s := getRandomBytes()
if !ib.Add(s) {
// ib is full.
break
}
items = append(items, string(s))
totalLen += len(s)
}
// Verify all the items are added.
if len(ib.items) != len(items) {
t.Fatalf("unexpected number of items added; got %d; want %d", len(ib.items), len(items))
}
if len(ib.data) != totalLen {
t.Fatalf("unexpected ib.data len; got %d; want %d", len(ib.data), totalLen)
}
data := ib.data
for j, it := range ib.items {
item := it.String(data)
if items[j] != item {
t.Fatalf("unexpected item at index %d out of %d, loop %d\ngot\n%X\nwant\n%X", j, len(items), i, item, items[j])
}
}
}
}
func TestInmemoryBlockSort(t *testing.T) {
var ib inmemoryBlock
for i := 0; i < 100; i++ {
var items []string
totalLen := 0
ib.Reset()
// Fill ib.
for j := 0; j < rand.Intn(1500); j++ {
s := getRandomBytes()
if !ib.Add(s) {
// ib is full.
break
}
items = append(items, string(s))
totalLen += len(s)
}
// Sort ib.
ib.sort()
sort.Strings(items)
// Verify items are sorted.
if len(ib.items) != len(items) {
t.Fatalf("unexpected number of items added; got %d; want %d", len(ib.items), len(items))
}
if len(ib.data) != totalLen {
t.Fatalf("unexpected ib.data len; got %d; want %d", len(ib.data), totalLen)
}
data := ib.data
for j, it := range ib.items {
item := it.String(data)
if items[j] != item {
t.Fatalf("unexpected item at index %d out of %d, loop %d\ngot\n%X\nwant\n%X", j, len(items), i, item, items[j])
}
}
}
}
func TestInmemoryBlockMarshalUnmarshal(t *testing.T) {
var ib, ib2 inmemoryBlock
var sb storageBlock
var firstItem, commonPrefix []byte
var itemsLen uint32
var mt marshalType
for i := 0; i < 1000; i++ {
var items []string
totalLen := 0
ib.Reset()
// Fill ib.
itemsCount := 2 * (rand.Intn(i+1) + 1)
for j := 0; j < itemsCount/2; j++ {
s := getRandomBytes()
s = []byte("prefix " + string(s))
if !ib.Add(s) {
// ib is full.
break
}
items = append(items, string(s))
totalLen += len(s)
s = getRandomBytes()
if !ib.Add(s) {
// ib is full
break
}
items = append(items, string(s))
totalLen += len(s)
}
// Marshal ib.
sort.Strings(items)
firstItem, commonPrefix, itemsLen, mt = ib.MarshalUnsortedData(&sb, firstItem[:0], commonPrefix[:0], 0)
if int(itemsLen) != len(ib.items) {
t.Fatalf("unexpected number of items marshaled; got %d; want %d", itemsLen, len(ib.items))
}
firstItemExpected := ib.items[0].String(ib.data)
if string(firstItem) != firstItemExpected {
t.Fatalf("unexpected the first item\ngot\n%q\nwant\n%q", firstItem, firstItemExpected)
}
if err := checkMarshalType(mt); err != nil {
t.Fatalf("invalid mt: %s", err)
}
// Unmarshal ib.
if err := ib2.UnmarshalData(&sb, firstItem, commonPrefix, itemsLen, mt); err != nil {
t.Fatalf("cannot unmarshal data for firstItem=%q, commonPrefix=%q, itemsLen=%d, mt=%d: %s",
firstItem, commonPrefix, itemsLen, mt, err)
}
// Verify all the items are sorted and unmarshaled.
if len(ib2.items) != len(items) {
t.Fatalf("unexpected number of items unmarshaled; got %d; want %d", len(ib2.items), len(items))
}
if len(ib2.data) != totalLen {
t.Fatalf("unexpected ib.data len; got %d; want %d", len(ib2.data), totalLen)
}
for j := range items {
it2 := ib2.items[j]
item2 := it2.String(ib2.data)
if len(items[j]) != len(item2) {
t.Fatalf("items length mismatch at index %d out of %d, loop %d\ngot\n(len=%d) %X\nwant\n(len=%d) %X",
j, len(items), i, len(item2), item2, len(items[j]), items[j])
}
}
for j, it := range ib2.items {
item := it.String(ib2.data)
if items[j] != string(item) {
t.Fatalf("unexpected item at index %d out of %d, loop %d\ngot\n(len=%d) %X\nwant\n(len=%d) %X",
j, len(items), i, len(item), item, len(items[j]), items[j])
}
}
}
}
func getRandomBytes() []byte {
rndLock.Lock()
iv, ok := quick.Value(bytesType, rnd)
rndLock.Unlock()
if !ok {
logger.Panicf("error in quick.Value when generating random string")
}
return iv.Interface().([]byte)
}
var bytesType = reflect.TypeOf([]byte(nil))
var (
rnd = rand.New(rand.NewSource(1))
rndLock sync.Mutex
)