Files
VictoriaMetrics/lib/storage/search_timing_test.go
Artem Fetishev 8c1c92d4c9 lib/storage: rewrite search benchmarks to allow to make it easy adding new cases (#9691)
Benchmarking storage search api requires taking into account many
parameters, such as:
- data configuration: how many series, deleted series, search time range
- where the index data recides: prev and or indexDB
- which search operation to measure

While adding a new benchmark use case involves a lot boilerplate code.

This pr implements a framework for testing storage search ops that can
be relatively easily extended. This come in expecially handy when adding
new cases for parition index.

The current set of params will result of a lot of benchmarks to be run
which most probably does not make sense because:
- it will take a lot of time and
- the output data is hard to compare manually.

However, these benchmarks are very useful when only small set of params
is of interest. For example, if I want to compare the search of 100k
metric names when the index data resides in prevOnly, currOnly or
prevAndCurr indexDBs. This would translate in the following cmd:

```shell
go test ./lib/storage --loggerLevel=ERROR -run=^$ -bench=^BenchmarkSearch/MetricNames/.*/VariableSeries/100000$
```

Why this change:
- I often need to run benchmarks with configs that I did not have
before, requires either modifying the existing one or writing a new one.
It is easy to get lost and make benchmark non-comparable
- I need some way to make legacy and pt index benchmarks comparable

Signed-off-by: Artem Fetishev <rtm@victoriametrics.com>
2025-09-22 15:05:32 +02:00

68 lines
1.8 KiB
Go

package storage
import (
"testing"
"github.com/google/go-cmp/cmp"
)
// benchmarkSearchData measures the search of all data on the given time range.
// It also ensures that the search result is correct by comparing it with metric
// rows stored in the database.
func benchmarkSearchData(b *testing.B, s *Storage, tr TimeRange, mrs []MetricRow) {
b.Helper()
tfss := NewTagFilters()
if err := tfss.Add([]byte("__name__"), []byte(".*"), false, true); err != nil {
b.Fatalf("unexpected error in TagFilters.Add: %v", err)
}
type metricBlock struct {
MetricName []byte
Block *Block
}
mbs := make([]metricBlock, 0, len(mrs))
for b.Loop() {
mbs = mbs[:0]
var search Search
search.Init(nil, s, []*TagFilters{tfss}, tr, 1e9, noDeadline)
for search.NextMetricBlock() {
var (
block Block
mb metricBlock
)
search.MetricBlockRef.BlockRef.MustReadBlock(&block)
mb.MetricName = append(mb.MetricName, search.MetricBlockRef.MetricName...)
mb.Block = &block
mbs = append(mbs, mb)
}
if err := search.Error(); err != nil {
b.Fatalf("search error: %v", err)
}
search.MustClose()
}
var mn MetricName
got := make([]MetricRow, len(mrs))
for i, mb := range mbs {
rb := newTestRawBlock(mb.Block, tr)
if err := mn.Unmarshal(mb.MetricName); err != nil {
b.Fatalf("cannot unmarshal MetricName %v: %v", string(mb.MetricName), err)
}
metricNameRaw := mn.marshalRaw(nil)
for j, timestamp := range rb.Timestamps {
mr := MetricRow{
MetricNameRaw: metricNameRaw,
Timestamp: timestamp,
Value: rb.Values[j],
}
got[i] = mr
}
}
testSortMetricRows(got)
want := mrs
testSortMetricRows(want)
if diff := cmp.Diff(mrsToString(want), mrsToString(got)); diff != "" {
b.Errorf("unexpected metric rows (-want, +got):\n%s", diff)
}
}