mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08: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:
parent
8be416a67c
commit
7427753922
|
@ -65,6 +65,7 @@ var newTestParserFns = map[string]newParser{
|
||||||
//
|
//
|
||||||
// NOTE(bwplotka): Do not try to conclude "what parser (OM, proto, prom) is the fastest"
|
// 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).
|
// 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) {
|
func BenchmarkParse(b *testing.B) {
|
||||||
for _, bcase := range []struct {
|
for _, bcase := range []struct {
|
||||||
dataFile string // Localized to "./testdata".
|
dataFile string // Localized to "./testdata".
|
||||||
|
@ -76,7 +77,7 @@ func BenchmarkParse(b *testing.B) {
|
||||||
{dataFile: "promtestdata.txt", parser: "promtext", compareToExpfmtFormat: expfmt.TypeTextPlain},
|
{dataFile: "promtestdata.txt", parser: "promtext", compareToExpfmtFormat: expfmt.TypeTextPlain},
|
||||||
{dataFile: "promtestdata.nometa.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"},
|
{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.
|
// We don't pass compareToExpfmtFormat: expfmt.TypeOpenMetrics as expfmt does not support OM exemplars, see https://github.com/prometheus/common/issues/703.
|
||||||
|
|
|
@ -25,6 +25,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -1386,8 +1388,17 @@ func TestScrapeLoopFailLegacyUnderUTF8(t *testing.T) {
|
||||||
require.Equal(t, 1, seriesAdded)
|
require.Equal(t, 1, seriesAdded)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeTestMetrics(n int) []byte {
|
func readTextParseTestMetrics(t testing.TB) []byte {
|
||||||
// Construct a metrics string to parse
|
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{}
|
sb := bytes.Buffer{}
|
||||||
fmt.Fprintf(&sb, "# TYPE metric_a gauge\n")
|
fmt.Fprintf(&sb, "# TYPE metric_a gauge\n")
|
||||||
fmt.Fprintf(&sb, "# HELP metric_a help text\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 {
|
func promTextToProto(tb testing.TB, text []byte) []byte {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
d := expfmt.NewDecoder(bytes.NewReader(text), expfmt.TextVersion)
|
var p expfmt.TextParser
|
||||||
|
fams, err := p.TextToMetricFamilies(bytes.NewReader(text))
|
||||||
pb := &dto.MetricFamily{}
|
|
||||||
if err := d.Decode(pb); err != nil {
|
|
||||||
tb.Fatal(err)
|
|
||||||
}
|
|
||||||
o, err := proto.Marshal(pb)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tb.Fatal(err)
|
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{}
|
buf := bytes.Buffer{}
|
||||||
// Write first length, then binary protobuf.
|
for _, n := range names {
|
||||||
varintBuf := binary.AppendUvarint(nil, uint64(len(o)))
|
o, err := proto.Marshal(fams[n])
|
||||||
buf.Write(varintBuf)
|
if err != nil {
|
||||||
buf.Write(o)
|
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()
|
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' \
|
-run '^$' -bench '^BenchmarkScrapeLoopAppend' \
|
||||||
-benchtime 5s -count 6 -cpu 2 -timeout 999m \
|
-benchtime 5s -count 6 -cpu 2 -timeout 999m \
|
||||||
| tee ${bench}.txt
|
| tee ${bench}.txt
|
||||||
*/
|
*/
|
||||||
func BenchmarkScrapeLoopAppend(b *testing.B) {
|
func BenchmarkScrapeLoopAppend(b *testing.B) {
|
||||||
metricsText := makeTestMetrics(100)
|
for _, data := range []struct {
|
||||||
|
name string
|
||||||
// Create proto representation.
|
parsableText []byte
|
||||||
metricsProto := promTextToProto(b, metricsText)
|
|
||||||
|
|
||||||
for _, bcase := range []struct {
|
|
||||||
name string
|
|
||||||
contentType string
|
|
||||||
parsable []byte
|
|
||||||
}{
|
}{
|
||||||
{name: "PromText", contentType: "text/plain", parsable: metricsText},
|
{name: "1Fam1000Gauges", parsableText: makeTestGauges(1000)}, // ~33.8 KB, ~38.8 KB in proto
|
||||||
{name: "OMText", contentType: "application/openmetrics-text", parsable: metricsText},
|
{name: "59FamsAllTypes", parsableText: readTextParseTestMetrics(b)}, // ~33.3 KB, ~13.2 KB in proto.
|
||||||
{name: "PromProto", contentType: "application/vnd.google.protobuf", parsable: metricsProto},
|
|
||||||
} {
|
} {
|
||||||
b.Run(fmt.Sprintf("fmt=%v", bcase.name), func(b *testing.B) {
|
b.Run(fmt.Sprintf("data=%v", data.name), func(b *testing.B) {
|
||||||
ctx, sl := simpleTestScrapeLoop(b)
|
metricsProto := promTextToProto(b, data.parsableText)
|
||||||
|
|
||||||
slApp := sl.appender(ctx)
|
for _, bcase := range []struct {
|
||||||
ts := time.Time{}
|
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()
|
slApp := sl.appender(ctx)
|
||||||
b.ResetTimer()
|
ts := time.Time{}
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
ts = ts.Add(time.Second)
|
b.ReportAllocs()
|
||||||
_, _, _, err := sl.append(slApp, bcase.parsable, bcase.contentType, ts)
|
b.ResetTimer()
|
||||||
if err != nil {
|
for i := 0; i < b.N; i++ {
|
||||||
b.Fatal(err)
|
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)
|
simpleStorage := teststorage.New(t)
|
||||||
defer simpleStorage.Close()
|
defer simpleStorage.Close()
|
||||||
|
|
||||||
metricsText := makeTestMetrics(10)
|
metricsText := makeTestGauges(10)
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
enableCompression bool
|
enableCompression bool
|
||||||
|
|
Loading…
Reference in a new issue