Merge pull request #12598 from bboreham/labels-json

Faster streaming of Labels to JSON, via jsoniter.
This commit is contained in:
Bryan Boreham 2023-08-02 09:53:19 +01:00 committed by GitHub
commit 87cbd26f6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 34 deletions

View file

@ -3417,27 +3417,57 @@ func TestReturnAPIError(t *testing.T) {
var testResponseWriter = httptest.ResponseRecorder{}
func BenchmarkRespond(b *testing.B) {
b.ReportAllocs()
request, err := http.NewRequest(http.MethodGet, "/does-not-matter", nil)
require.NoError(b, err)
points := []promql.FPoint{}
for i := 0; i < 10000; i++ {
points = append(points, promql.FPoint{F: float64(i * 1000000), T: int64(i)})
}
response := &QueryData{
ResultType: parser.ValueTypeMatrix,
Result: promql.Matrix{
promql.Series{
Floats: points,
Metric: labels.EmptyLabels(),
},
},
matrix := promql.Matrix{}
for i := 0; i < 1000; i++ {
matrix = append(matrix, promql.Series{
Metric: labels.FromStrings("__name__", fmt.Sprintf("series%v", i),
"label", fmt.Sprintf("series%v", i),
"label2", fmt.Sprintf("series%v", i)),
Floats: points[:10],
})
}
b.ResetTimer()
api := API{}
api.InstallCodec(JSONCodec{})
for n := 0; n < b.N; n++ {
api.respond(&testResponseWriter, request, response, nil)
series := []labels.Labels{}
for i := 0; i < 1000; i++ {
series = append(series, labels.FromStrings("__name__", fmt.Sprintf("series%v", i),
"label", fmt.Sprintf("series%v", i),
"label2", fmt.Sprintf("series%v", i)))
}
cases := []struct {
name string
response interface{}
}{
{name: "10000 points no labels", response: &QueryData{
ResultType: parser.ValueTypeMatrix,
Result: promql.Matrix{
promql.Series{
Floats: points,
Metric: labels.EmptyLabels(),
},
},
}},
{name: "1000 labels", response: series},
{name: "1000 series 10 points", response: &QueryData{
ResultType: parser.ValueTypeMatrix,
Result: matrix,
}},
}
for _, c := range cases {
b.Run(c.name, func(b *testing.B) {
b.ReportAllocs()
request, err := http.NewRequest(http.MethodGet, "/does-not-matter", nil)
require.NoError(b, err)
b.ResetTimer()
api := API{}
api.InstallCodec(JSONCodec{})
for n := 0; n < b.N; n++ {
api.respond(&testResponseWriter, request, c.response, nil)
}
})
}
}

View file

@ -19,6 +19,7 @@ import (
jsoniter "github.com/json-iterator/go"
"github.com/prometheus/prometheus/model/exemplar"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/util/jsonutil"
)
@ -29,6 +30,7 @@ func init() {
jsoniter.RegisterTypeEncoderFunc("promql.FPoint", marshalFPointJSON, marshalPointJSONIsEmpty)
jsoniter.RegisterTypeEncoderFunc("promql.HPoint", marshalHPointJSON, marshalPointJSONIsEmpty)
jsoniter.RegisterTypeEncoderFunc("exemplar.Exemplar", marshalExemplarJSON, marshalExemplarJSONEmpty)
jsoniter.RegisterTypeEncoderFunc("labels.Labels", unsafeMarshalLabelsJSON, labelsIsEmpty)
}
// JSONCodec is a Codec that encodes API responses as JSON.
@ -68,12 +70,7 @@ func marshalSeriesJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
s := *((*promql.Series)(ptr))
stream.WriteObjectStart()
stream.WriteObjectField(`metric`)
m, err := s.Metric.MarshalJSON()
if err != nil {
stream.Error = err
return
}
stream.SetBuffer(append(stream.Buffer(), m...))
marshalLabelsJSON(s.Metric, stream)
for i, p := range s.Floats {
stream.WriteMore()
@ -129,12 +126,7 @@ func marshalSampleJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
s := *((*promql.Sample)(ptr))
stream.WriteObjectStart()
stream.WriteObjectField(`metric`)
m, err := s.Metric.MarshalJSON()
if err != nil {
stream.Error = err
return
}
stream.SetBuffer(append(stream.Buffer(), m...))
marshalLabelsJSON(s.Metric, stream)
stream.WriteMore()
if s.H == nil {
stream.WriteObjectField(`value`)
@ -194,12 +186,7 @@ func marshalExemplarJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
// "labels" key.
stream.WriteObjectField(`labels`)
lbls, err := p.Labels.MarshalJSON()
if err != nil {
stream.Error = err
return
}
stream.SetBuffer(append(stream.Buffer(), lbls...))
marshalLabelsJSON(p.Labels, stream)
// "value" key.
stream.WriteMore()
@ -217,3 +204,28 @@ func marshalExemplarJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
func marshalExemplarJSONEmpty(unsafe.Pointer) bool {
return false
}
func unsafeMarshalLabelsJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
labelsPtr := (*labels.Labels)(ptr)
marshalLabelsJSON(*labelsPtr, stream)
}
func marshalLabelsJSON(lbls labels.Labels, stream *jsoniter.Stream) {
stream.WriteObjectStart()
i := 0
lbls.Range(func(v labels.Label) {
if i != 0 {
stream.WriteMore()
}
i++
stream.WriteString(v.Name)
stream.WriteRaw(`:`)
stream.WriteString(v.Value)
})
stream.WriteObjectEnd()
}
func labelsIsEmpty(ptr unsafe.Pointer) bool {
labelsPtr := (*labels.Labels)(ptr)
return labelsPtr.IsEmpty()
}