prometheus/labels/labels_test.go

207 lines
3.8 KiB
Go

package labels
import (
"fmt"
"io/ioutil"
"math/rand"
"os"
"sort"
"testing"
"unsafe"
"github.com/pkg/errors"
promlabels "github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/pkg/textparse"
"github.com/stretchr/testify/require"
)
func TestCompare(t *testing.T) {
cases := []struct {
a, b []Label
res int
}{
{
a: []Label{},
b: []Label{},
res: 0,
},
{
a: []Label{{"a", ""}},
b: []Label{{"a", ""}, {"b", ""}},
res: -1,
},
{
a: []Label{{"a", ""}},
b: []Label{{"a", ""}},
res: 0,
},
{
a: []Label{{"aa", ""}, {"aa", ""}},
b: []Label{{"aa", ""}, {"ab", ""}},
res: -1,
},
{
a: []Label{{"aa", ""}, {"abb", ""}},
b: []Label{{"aa", ""}, {"ab", ""}},
res: 1,
},
{
a: []Label{
{"__name__", "go_gc_duration_seconds"},
{"job", "prometheus"},
{"quantile", "0.75"},
},
b: []Label{
{"__name__", "go_gc_duration_seconds"},
{"job", "prometheus"},
{"quantile", "1"},
},
res: -1,
},
{
a: []Label{
{"handler", "prometheus"},
{"instance", "localhost:9090"},
},
b: []Label{
{"handler", "query"},
{"instance", "localhost:9090"},
},
res: -1,
},
}
for _, c := range cases {
// Use constructor to ensure sortedness.
a, b := New(c.a...), New(c.b...)
require.Equal(t, c.res, Compare(a, b))
}
}
func BenchmarkSliceSort(b *testing.B) {
lbls, err := readPrometheusLabels("../testdata/1m.series", 900000)
require.NoError(b, err)
for len(lbls) < 20e6 {
lbls = append(lbls, lbls...)
}
for i := range lbls {
j := rand.Intn(i + 1)
lbls[i], lbls[j] = lbls[j], lbls[i]
}
for _, k := range []int{
100, 5000, 50000, 300000, 900000, 5e6, 20e6,
} {
b.Run(fmt.Sprintf("%d", k), func(b *testing.B) {
b.ReportAllocs()
for a := 0; a < b.N; a++ {
b.StopTimer()
cl := make(Slice, k)
copy(cl, Slice(lbls[:k]))
b.StartTimer()
sort.Sort(cl)
}
})
}
}
func readPrometheusLabels(fn string, n int) ([]Labels, error) {
f, err := os.Open(fn)
if err != nil {
return nil, err
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
p := textparse.New(b)
i := 0
var mets []Labels
hashes := map[uint64]struct{}{}
for p.Next() && i < n {
m := make(Labels, 0, 10)
p.Metric((*promlabels.Labels)(unsafe.Pointer(&m)))
h := m.Hash()
if _, ok := hashes[h]; ok {
continue
}
mets = append(mets, m)
hashes[h] = struct{}{}
i++
}
if err := p.Err(); err != nil {
return nil, err
}
if i != n {
return mets, errors.Errorf("requested %d metrics but found %d", n, i)
}
return mets, nil
}
func BenchmarkLabelSetFromMap(b *testing.B) {
m := map[string]string{
"job": "node",
"instance": "123.123.1.211:9090",
"path": "/api/v1/namespaces/<namespace>/deployments/<name>",
"method": "GET",
"namespace": "system",
"status": "500",
}
var ls Labels
b.ReportAllocs()
for i := 0; i < b.N; i++ {
ls = FromMap(m)
}
_ = ls
}
func BenchmarkMapFromLabels(b *testing.B) {
m := map[string]string{
"job": "node",
"instance": "123.123.1.211:9090",
"path": "/api/v1/namespaces/<namespace>/deployments/<name>",
"method": "GET",
"namespace": "system",
"status": "500",
}
ls := FromMap(m)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
m = ls.Map()
}
}
func BenchmarkLabelSetEquals(b *testing.B) {
// The vast majority of comparisons will be against a matching label set.
m := map[string]string{
"job": "node",
"instance": "123.123.1.211:9090",
"path": "/api/v1/namespaces/<namespace>/deployments/<name>",
"method": "GET",
"namespace": "system",
"status": "500",
}
ls := FromMap(m)
var res bool
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
res = ls.Equals(ls)
}
_ = res
}