1
0
mirror of https://github.com/prometheus/prometheus.git synced 2025-02-06 10:24:02 +00:00

scrape: Add realistic data case for scrape loop append bench. (#15966)

* scrape: Add realistic data case for scrape loop append bench.

Signed-off-by: bwplotka <bwplotka@gmail.com>

* Update scrape/scrape_test.go

Co-authored-by: George Krajcsovits <krajorama@users.noreply.github.com>
Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com>

---------

Signed-off-by: bwplotka <bwplotka@gmail.com>
Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com>
Co-authored-by: George Krajcsovits <krajorama@users.noreply.github.com>
This commit is contained in:
Bartlomiej Plotka 2025-02-03 15:46:39 +01:00 committed by GitHub
parent 8be416a67c
commit 7427753922
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 95 additions and 40 deletions

View File

@ -65,6 +65,7 @@ var newTestParserFns = map[string]newParser{
//
// NOTE(bwplotka): Do not try to conclude "what parser (OM, proto, prom) is the fastest"
// as the testdata has different amount and type of metrics and features (e.g. exemplars).
// Use scrape.BenchmarkScrapeLoopAppend for this purpose.
func BenchmarkParse(b *testing.B) {
for _, bcase := range []struct {
dataFile string // Localized to "./testdata".
@ -76,7 +77,7 @@ func BenchmarkParse(b *testing.B) {
{dataFile: "promtestdata.txt", parser: "promtext", compareToExpfmtFormat: expfmt.TypeTextPlain},
{dataFile: "promtestdata.nometa.txt", parser: "promtext", compareToExpfmtFormat: expfmt.TypeTextPlain},
// We don't pass compareToExpfmtFormat: expfmt.TypeProtoDelim as expfmt does not support GAUGE_HISTOGRAM, see https://github.com/prometheus/common/issues/430.
// We don't pass compareToExpfmtFormat: expfmt.TypeProtoDelim as expfmt does not support GAUGE_HISTOGRAM, see https://github.com/prometheus/common/issues/430.
{dataProto: createTestProtoBuf(b).Bytes(), parser: "promproto"},
// We don't pass compareToExpfmtFormat: expfmt.TypeOpenMetrics as expfmt does not support OM exemplars, see https://github.com/prometheus/common/issues/703.

View File

@ -25,6 +25,8 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"sort"
"strconv"
"strings"
"sync"
@ -1386,8 +1388,17 @@ func TestScrapeLoopFailLegacyUnderUTF8(t *testing.T) {
require.Equal(t, 1, seriesAdded)
}
func makeTestMetrics(n int) []byte {
// Construct a metrics string to parse
func readTextParseTestMetrics(t testing.TB) []byte {
t.Helper()
b, err := os.ReadFile("../model/textparse/testdata/promtestdata.txt")
if err != nil {
t.Fatal(err)
}
return b
}
func makeTestGauges(n int) []byte {
sb := bytes.Buffer{}
fmt.Fprintf(&sb, "# TYPE metric_a gauge\n")
fmt.Fprintf(&sb, "# HELP metric_a help text\n")
@ -1401,59 +1412,102 @@ func makeTestMetrics(n int) []byte {
func promTextToProto(tb testing.TB, text []byte) []byte {
tb.Helper()
d := expfmt.NewDecoder(bytes.NewReader(text), expfmt.TextVersion)
pb := &dto.MetricFamily{}
if err := d.Decode(pb); err != nil {
tb.Fatal(err)
}
o, err := proto.Marshal(pb)
var p expfmt.TextParser
fams, err := p.TextToMetricFamilies(bytes.NewReader(text))
if err != nil {
tb.Fatal(err)
}
// Order by name for the deterministic tests.
var names []string
for n := range fams {
names = append(names, n)
}
sort.Strings(names)
buf := bytes.Buffer{}
// Write first length, then binary protobuf.
varintBuf := binary.AppendUvarint(nil, uint64(len(o)))
buf.Write(varintBuf)
buf.Write(o)
for _, n := range names {
o, err := proto.Marshal(fams[n])
if err != nil {
tb.Fatal(err)
}
// Write first length, then binary protobuf.
varintBuf := binary.AppendUvarint(nil, uint64(len(o)))
buf.Write(varintBuf)
buf.Write(o)
}
return buf.Bytes()
}
func TestPromTextToProto(t *testing.T) {
metricsText := readTextParseTestMetrics(t)
// TODO(bwplotka): Windows adds \r for new lines which is
// not handled correctly in the expfmt parser, fix it.
metricsText = bytes.ReplaceAll(metricsText, []byte("\r"), nil)
metricsProto := promTextToProto(t, metricsText)
d := expfmt.NewDecoder(bytes.NewReader(metricsProto), expfmt.NewFormat(expfmt.TypeProtoDelim))
var got []string
for {
mf := &dto.MetricFamily{}
if err := d.Decode(mf); err != nil {
if errors.Is(err, io.EOF) {
break
}
t.Fatal(err)
}
got = append(got, mf.GetName())
}
require.Len(t, got, 59)
// Check a few to see if those are not dups.
require.Equal(t, "go_gc_duration_seconds", got[0])
require.Equal(t, "prometheus_evaluator_duration_seconds", got[32])
require.Equal(t, "prometheus_treecache_zookeeper_failures_total", got[58])
}
/*
export bench=scrape-loop-v1 && go test \
export bench=append-v1 && go test \
-run '^$' -bench '^BenchmarkScrapeLoopAppend' \
-benchtime 5s -count 6 -cpu 2 -timeout 999m \
| tee ${bench}.txt
*/
func BenchmarkScrapeLoopAppend(b *testing.B) {
metricsText := makeTestMetrics(100)
// Create proto representation.
metricsProto := promTextToProto(b, metricsText)
for _, bcase := range []struct {
name string
contentType string
parsable []byte
for _, data := range []struct {
name string
parsableText []byte
}{
{name: "PromText", contentType: "text/plain", parsable: metricsText},
{name: "OMText", contentType: "application/openmetrics-text", parsable: metricsText},
{name: "PromProto", contentType: "application/vnd.google.protobuf", parsable: metricsProto},
{name: "1Fam1000Gauges", parsableText: makeTestGauges(1000)}, // ~33.8 KB, ~38.8 KB in proto
{name: "59FamsAllTypes", parsableText: readTextParseTestMetrics(b)}, // ~33.3 KB, ~13.2 KB in proto.
} {
b.Run(fmt.Sprintf("fmt=%v", bcase.name), func(b *testing.B) {
ctx, sl := simpleTestScrapeLoop(b)
b.Run(fmt.Sprintf("data=%v", data.name), func(b *testing.B) {
metricsProto := promTextToProto(b, data.parsableText)
slApp := sl.appender(ctx)
ts := time.Time{}
for _, bcase := range []struct {
name string
contentType string
parsable []byte
}{
{name: "PromText", contentType: "text/plain", parsable: data.parsableText},
{name: "OMText", contentType: "application/openmetrics-text", parsable: data.parsableText},
{name: "PromProto", contentType: "application/vnd.google.protobuf", parsable: metricsProto},
} {
b.Run(fmt.Sprintf("fmt=%v", bcase.name), func(b *testing.B) {
ctx, sl := simpleTestScrapeLoop(b)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
ts = ts.Add(time.Second)
_, _, _, err := sl.append(slApp, bcase.parsable, bcase.contentType, ts)
if err != nil {
b.Fatal(err)
}
slApp := sl.appender(ctx)
ts := time.Time{}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
ts = ts.Add(time.Second)
_, _, _, err := sl.append(slApp, bcase.parsable, bcase.contentType, ts)
if err != nil {
b.Fatal(err)
}
}
})
}
})
}
@ -4474,7 +4528,7 @@ func TestScrapeLoopCompression(t *testing.T) {
simpleStorage := teststorage.New(t)
defer simpleStorage.Close()
metricsText := makeTestMetrics(10)
metricsText := makeTestGauges(10)
for _, tc := range []struct {
enableCompression bool