Merge pull request #12598 from bboreham/labels-json
Faster streaming of Labels to JSON, via jsoniter.
This commit is contained in:
commit
87cbd26f6b
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue