Skip to content

Commit 87e7482

Browse files
authored
expfmt: precompute parsed goautoneg.Accept for well-known Format constants (#967)
Repeatedly parsing static Format constants with goautoneg.ParseAccept in matchFormat on every call causes unnecessary heap allocations and latency during content negotiation. Precompute the parsed goautoneg.Accept structs for standard Format constants at package initialization time, falling back to goautoneg.ParseAccept for custom formats. Also add benchmarks with b.ReportAllocs() to track negotiation performance. Signed-off-by: David Ashpole <dashpole@google.com>
1 parent 7832844 commit 87e7482

2 files changed

Lines changed: 57 additions & 5 deletions

File tree

expfmt/encode.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@ import (
2727
"github.com/prometheus/common/model"
2828
)
2929

30+
var formatToAccept = map[Format]goautoneg.Accept{}
31+
32+
func init() {
33+
for _, f := range []Format{
34+
FmtText,
35+
FmtProtoDelim,
36+
FmtProtoText,
37+
FmtProtoCompact,
38+
FmtOpenMetrics_1_0_0,
39+
fmtOpenMetrics_2_0_0,
40+
FmtOpenMetrics_0_0_1,
41+
} {
42+
if parsed := goautoneg.ParseAccept(string(f)); len(parsed) > 0 {
43+
formatToAccept[f] = parsed[0]
44+
}
45+
}
46+
}
47+
3048
// Encoder types encode metric families into an underlying wire protocol.
3149
type Encoder interface {
3250
Encode(*dto.MetricFamily) error
@@ -81,7 +99,7 @@ func NegotiateIncludingOpenMetrics(h http.Header) Format {
8199
// format if present in the accepted list, or the first accepted format (or FmtText
82100
// if accepted is empty).
83101
func NegotiateAccept(h http.Header, accepted ...Format) Format {
84-
escapingScheme := Format(fmt.Sprintf("; escaping=%s", Format(model.NameEscapingScheme.String())))
102+
escapingScheme := Format("; escaping=" + model.NameEscapingScheme.String())
85103
for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
86104
if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" {
87105
switch Format(escapeParam) {
@@ -111,11 +129,14 @@ func NegotiateAccept(h http.Header, accepted ...Format) Format {
111129

112130
// matchFormat checks if a parsed accept clause matches a given Format.
113131
func matchFormat(ac goautoneg.Accept, f Format) bool {
114-
parsed := goautoneg.ParseAccept(string(f))
115-
if len(parsed) == 0 {
116-
return false
132+
target, ok := formatToAccept[f]
133+
if !ok {
134+
parsed := goautoneg.ParseAccept(string(f))
135+
if len(parsed) == 0 {
136+
return false
137+
}
138+
target = parsed[0]
117139
}
118-
target := parsed[0]
119140

120141
if ac.Type != "*" && ac.Type != target.Type {
121142
return false

expfmt/encode_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,3 +583,34 @@ func TestDottedEncode(t *testing.T) {
583583
}
584584
}
585585
}
586+
587+
func BenchmarkNegotiate(b *testing.B) {
588+
h := http.Header{}
589+
h.Set(hdrAccept, "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3,application/json;q=0.1,*/*;q=0.01")
590+
b.ReportAllocs()
591+
b.ResetTimer()
592+
for b.Loop() {
593+
_ = Negotiate(h)
594+
}
595+
}
596+
597+
func BenchmarkNegotiateIncludingOpenMetrics(b *testing.B) {
598+
h := http.Header{}
599+
h.Set(hdrAccept, "application/openmetrics-text;version=1.0.0;q=0.8,application/openmetrics-text;version=0.0.1;q=0.5,text/plain;version=0.0.4;q=0.3,*/*;q=0.1")
600+
b.ReportAllocs()
601+
b.ResetTimer()
602+
for b.Loop() {
603+
_ = NegotiateIncludingOpenMetrics(h)
604+
}
605+
}
606+
607+
func BenchmarkNegotiateAccept(b *testing.B) {
608+
h := http.Header{}
609+
h.Set(hdrAccept, "application/openmetrics-text;version=1.0.0;q=0.8,text/plain;version=0.0.4;q=0.3,*/*;q=0.1")
610+
accepted := []Format{FmtOpenMetrics_1_0_0, FmtOpenMetrics_0_0_1, FmtProtoDelim, FmtProtoText, FmtProtoCompact, FmtText}
611+
b.ReportAllocs()
612+
b.ResetTimer()
613+
for b.Loop() {
614+
_ = NegotiateAccept(h, accepted...)
615+
}
616+
}

0 commit comments

Comments
 (0)