Files
VictoriaMetrics/lib/storage/index_db_timing_test.go
2026-02-18 14:28:29 +01:00

318 lines
8.9 KiB
Go

package storage
import (
"fmt"
"regexp"
"strconv"
"testing"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
)
func BenchmarkRegexpFilterMatch(b *testing.B) {
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
re := regexp.MustCompile(`.*foo-bar-baz.*`)
b := []byte("fdsffd foo-bar-baz assd fdsfad dasf dsa")
for pb.Next() {
if !re.Match(b) {
panic("BUG: regexp must match!")
}
b[0]++
}
})
}
func BenchmarkRegexpFilterMismatch(b *testing.B) {
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
re := regexp.MustCompile(`.*foo-bar-baz.*`)
b := []byte("fdsffd foo-bar sfddsf assd nmn,mfdsdsakj")
for pb.Next() {
if re.Match(b) {
panic("BUG: regexp mustn't match!")
}
b[0]++
}
})
}
func BenchmarkIndexDBAddTSIDs(b *testing.B) {
const path = "BenchmarkIndexDBAddTSIDs"
timestamp := time.Date(2025, 3, 17, 0, 0, 0, 0, time.UTC).UnixMilli()
s := MustOpenStorage(path, OpenOptions{})
ptw := s.tb.MustGetPartition(timestamp)
db := ptw.pt.idb
const recordsPerLoop = 1e3
b.ReportAllocs()
b.SetBytes(recordsPerLoop)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
var mn MetricName
var tsid TSID
// The most common tags.
mn.Tags = []Tag{
{
Key: []byte("job"),
},
{
Key: []byte("instance"),
},
}
startOffset := 0
for pb.Next() {
benchmarkIndexDBAddTSIDs(db, &tsid, &mn, timestamp, startOffset, recordsPerLoop)
startOffset += recordsPerLoop
}
})
b.StopTimer()
s.tb.PutPartition(ptw)
s.MustClose()
fs.MustRemoveDir(path)
}
func benchmarkIndexDBAddTSIDs(db *indexDB, tsid *TSID, mn *MetricName, timestamp int64, startOffset, recordsPerLoop int) {
date := uint64(timestamp) / msecPerDay
for i := range recordsPerLoop {
mn.MetricGroup = strconv.AppendUint(mn.MetricGroup[:0], uint64(i+startOffset), 10)
for j := range mn.Tags {
mn.Tags[j].Value = strconv.AppendUint(mn.Tags[j].Value[:0], uint64(i*j), 16)
}
mn.sortTags()
generateTSID(tsid, mn)
createAllIndexesForMetricName(db, mn, tsid, date)
}
}
func BenchmarkHeadPostingForMatchers(b *testing.B) {
// This benchmark is equivalent to https://github.com/prometheus/prometheus/blob/23c0299d85bfeb5d9b59e994861553a25ca578e5/tsdb/head_bench_test.go#L52
// See https://www.robustperception.io/evaluating-performance-and-correctness for more details.
const path = "BenchmarkHeadPostingForMatchers"
timestamp := int64(0)
s := MustOpenStorage(path, OpenOptions{})
ptw := s.tb.MustGetPartition(timestamp)
db := ptw.pt.idb
// Fill the db with data as in https://github.com/prometheus/prometheus/blob/23c0299d85bfeb5d9b59e994861553a25ca578e5/tsdb/head_bench_test.go#L66
var mn MetricName
var tsid TSID
date := uint64(timestamp) / msecPerDay
addSeries := func(kvs ...string) {
mn.Reset()
for i := 0; i < len(kvs); i += 2 {
mn.AddTag(kvs[i], kvs[i+1])
}
mn.sortTags()
generateTSID(&tsid, &mn)
createAllIndexesForMetricName(db, &mn, &tsid, date)
}
for n := range 10 {
ns := strconv.Itoa(n)
for i := range 100000 {
ix := strconv.Itoa(i)
addSeries("i", ix, "n", ns, "j", "foo")
// Have some series that won't be matched, to properly test inverted matches.
addSeries("i", ix, "n", ns, "j", "bar")
addSeries("i", ix, "n", "0_"+ns, "j", "bar")
addSeries("i", ix, "n", "1_"+ns, "j", "bar")
addSeries("i", ix, "n", "2_"+ns, "j", "foo")
}
}
// Make sure all the items can be searched.
s.DebugFlush()
b.ResetTimer()
benchSearch := func(b *testing.B, tfs *TagFilters, expectedMetricIDs int) {
tfss := []*TagFilters{tfs}
// Use special globalIndexTimeRange to instruct indexDB to search global
// index instead of per-day index.
tr := globalIndexTimeRange
for range b.N {
is := db.getIndexSearch(noDeadline)
metricIDs, err := is.searchMetricIDs(nil, tfss, tr, 2e9)
db.putIndexSearch(is)
if err != nil {
b.Fatalf("unexpected error in searchMetricIDs: %s", err)
}
if metricIDs.Len() != expectedMetricIDs {
b.Fatalf("unexpected metricIDs found; got %d; want %d", metricIDs.Len(), expectedMetricIDs)
}
}
}
addTagFilter := func(tfs *TagFilters, key, value string, isNegative, isRegexp bool) {
if err := tfs.Add([]byte(key), []byte(value), isNegative, isRegexp); err != nil {
b.Fatalf("cannot add tag filter %q=%q, isNegative=%v, isRegexp=%v", key, value, isNegative, isRegexp)
}
}
b.Run(`n="1"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
benchSearch(b, tfs, 2e5)
})
b.Run(`n="1",j="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "j", "foo", false, false)
benchSearch(b, tfs, 1e5)
})
b.Run(`j="foo",n="1"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "j", "foo", false, false)
addTagFilter(tfs, "n", "1", false, false)
benchSearch(b, tfs, 1e5)
})
b.Run(`n="1",j!="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "j", "foo", true, false)
benchSearch(b, tfs, 1e5)
})
b.Run(`i=~".*"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "i", ".*", false, true)
benchSearch(b, tfs, 0)
})
b.Run(`i=~".+"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "i", ".+", false, true)
benchSearch(b, tfs, 5e6)
})
b.Run(`i=~""`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "i", "", false, true)
benchSearch(b, tfs, 0)
})
b.Run(`i!=""`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "i", "", true, false)
benchSearch(b, tfs, 5e6)
})
b.Run(`n="1",i=~".*",j="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "i", ".*", false, true)
addTagFilter(tfs, "j", "foo", false, false)
benchSearch(b, tfs, 1e5)
})
b.Run(`n="1",i=~".*",i!="2",j="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "i", ".*", false, true)
addTagFilter(tfs, "i", "2", true, false)
addTagFilter(tfs, "j", "foo", false, false)
benchSearch(b, tfs, 1e5-1)
})
b.Run(`n="1",i!=""`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "i", "", true, false)
benchSearch(b, tfs, 2e5)
})
b.Run(`n="1",i!="",j="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "i", "", true, false)
addTagFilter(tfs, "j", "foo", false, false)
benchSearch(b, tfs, 1e5)
})
b.Run(`n="1",i=~".+",j="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "i", ".+", false, true)
addTagFilter(tfs, "j", "foo", false, false)
benchSearch(b, tfs, 1e5)
})
b.Run(`n="1",i=~"1.+",j="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "i", "1.+", false, true)
addTagFilter(tfs, "j", "foo", false, false)
benchSearch(b, tfs, 11110)
})
b.Run(`n="1",i=~".+",i!="2",j="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "i", ".+", false, true)
addTagFilter(tfs, "i", "2", true, false)
addTagFilter(tfs, "j", "foo", false, false)
benchSearch(b, tfs, 1e5-1)
})
b.Run(`n="1",i=~".+",i!~"2.*",j="foo"`, func(b *testing.B) {
tfs := NewTagFilters()
addTagFilter(tfs, "n", "1", false, false)
addTagFilter(tfs, "i", ".+", false, true)
addTagFilter(tfs, "i", "2.*", true, true)
addTagFilter(tfs, "j", "foo", false, false)
benchSearch(b, tfs, 88889)
})
s.tb.PutPartition(ptw)
s.MustClose()
fs.MustRemoveDir(path)
}
func BenchmarkIndexDBGetTSIDs(b *testing.B) {
const path = "BenchmarkIndexDBGetTSIDs"
timestamp := time.Date(2025, 3, 17, 0, 0, 0, 0, time.UTC).UnixMilli()
s := MustOpenStorage(path, OpenOptions{})
ptw := s.tb.MustGetPartition(timestamp)
db := ptw.pt.idb
const recordsPerLoop = 1000
const recordsCount = 1e5
// Fill the db with recordsCount records.
var mn MetricName
mn.MetricGroup = []byte("rps")
for i := range 2 {
key := fmt.Sprintf("key_%d", i)
value := fmt.Sprintf("value_%d", i)
mn.AddTag(key, value)
}
mn.sortTags()
var tsid TSID
date := uint64(timestamp) / msecPerDay
for range int(recordsCount) {
generateTSID(&tsid, &mn)
createAllIndexesForMetricName(db, &mn, &tsid, date)
}
db.s.DebugFlush()
b.SetBytes(recordsPerLoop)
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
var tsidLocal TSID
var metricNameLocal []byte
var mnLocal MetricName
mnLocal.CopyFrom(&mn)
mnLocal.sortTags()
for pb.Next() {
is := db.getIndexSearch(noDeadline)
for i := range recordsPerLoop {
metricNameLocal = mnLocal.Marshal(metricNameLocal[:0])
if !is.getTSIDByMetricName(&tsidLocal, metricNameLocal, date) {
panic(fmt.Errorf("cannot obtain tsid for row %d", i))
}
}
db.putIndexSearch(is)
}
})
b.StopTimer()
s.tb.PutPartition(ptw)
s.MustClose()
fs.MustRemoveDir(path)
}