mirror of https://github.com/prometheus/prometheus
Merge pull request #12598 from bboreham/labels-json
Faster streaming of Labels to JSON, via jsoniter.pull/12637/head
commit
87cbd26f6b
|
@ -3417,27 +3417,57 @@ func TestReturnAPIError(t *testing.T) {
|
||||||
var testResponseWriter = httptest.ResponseRecorder{}
|
var testResponseWriter = httptest.ResponseRecorder{}
|
||||||
|
|
||||||
func BenchmarkRespond(b *testing.B) {
|
func BenchmarkRespond(b *testing.B) {
|
||||||
b.ReportAllocs()
|
|
||||||
request, err := http.NewRequest(http.MethodGet, "/does-not-matter", nil)
|
|
||||||
require.NoError(b, err)
|
|
||||||
points := []promql.FPoint{}
|
points := []promql.FPoint{}
|
||||||
for i := 0; i < 10000; i++ {
|
for i := 0; i < 10000; i++ {
|
||||||
points = append(points, promql.FPoint{F: float64(i * 1000000), T: int64(i)})
|
points = append(points, promql.FPoint{F: float64(i * 1000000), T: int64(i)})
|
||||||
}
|
}
|
||||||
response := &QueryData{
|
matrix := promql.Matrix{}
|
||||||
ResultType: parser.ValueTypeMatrix,
|
for i := 0; i < 1000; i++ {
|
||||||
Result: promql.Matrix{
|
matrix = append(matrix, promql.Series{
|
||||||
promql.Series{
|
Metric: labels.FromStrings("__name__", fmt.Sprintf("series%v", i),
|
||||||
Floats: points,
|
"label", fmt.Sprintf("series%v", i),
|
||||||
Metric: labels.EmptyLabels(),
|
"label2", fmt.Sprintf("series%v", i)),
|
||||||
},
|
Floats: points[:10],
|
||||||
},
|
})
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
series := []labels.Labels{}
|
||||||
api := API{}
|
for i := 0; i < 1000; i++ {
|
||||||
api.InstallCodec(JSONCodec{})
|
series = append(series, labels.FromStrings("__name__", fmt.Sprintf("series%v", i),
|
||||||
for n := 0; n < b.N; n++ {
|
"label", fmt.Sprintf("series%v", i),
|
||||||
api.respond(&testResponseWriter, request, response, nil)
|
"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"
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/model/exemplar"
|
"github.com/prometheus/prometheus/model/exemplar"
|
||||||
|
"github.com/prometheus/prometheus/model/labels"
|
||||||
"github.com/prometheus/prometheus/promql"
|
"github.com/prometheus/prometheus/promql"
|
||||||
"github.com/prometheus/prometheus/util/jsonutil"
|
"github.com/prometheus/prometheus/util/jsonutil"
|
||||||
)
|
)
|
||||||
|
@ -29,6 +30,7 @@ func init() {
|
||||||
jsoniter.RegisterTypeEncoderFunc("promql.FPoint", marshalFPointJSON, marshalPointJSONIsEmpty)
|
jsoniter.RegisterTypeEncoderFunc("promql.FPoint", marshalFPointJSON, marshalPointJSONIsEmpty)
|
||||||
jsoniter.RegisterTypeEncoderFunc("promql.HPoint", marshalHPointJSON, marshalPointJSONIsEmpty)
|
jsoniter.RegisterTypeEncoderFunc("promql.HPoint", marshalHPointJSON, marshalPointJSONIsEmpty)
|
||||||
jsoniter.RegisterTypeEncoderFunc("exemplar.Exemplar", marshalExemplarJSON, marshalExemplarJSONEmpty)
|
jsoniter.RegisterTypeEncoderFunc("exemplar.Exemplar", marshalExemplarJSON, marshalExemplarJSONEmpty)
|
||||||
|
jsoniter.RegisterTypeEncoderFunc("labels.Labels", unsafeMarshalLabelsJSON, labelsIsEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONCodec is a Codec that encodes API responses as JSON.
|
// 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))
|
s := *((*promql.Series)(ptr))
|
||||||
stream.WriteObjectStart()
|
stream.WriteObjectStart()
|
||||||
stream.WriteObjectField(`metric`)
|
stream.WriteObjectField(`metric`)
|
||||||
m, err := s.Metric.MarshalJSON()
|
marshalLabelsJSON(s.Metric, stream)
|
||||||
if err != nil {
|
|
||||||
stream.Error = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.SetBuffer(append(stream.Buffer(), m...))
|
|
||||||
|
|
||||||
for i, p := range s.Floats {
|
for i, p := range s.Floats {
|
||||||
stream.WriteMore()
|
stream.WriteMore()
|
||||||
|
@ -129,12 +126,7 @@ func marshalSampleJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||||
s := *((*promql.Sample)(ptr))
|
s := *((*promql.Sample)(ptr))
|
||||||
stream.WriteObjectStart()
|
stream.WriteObjectStart()
|
||||||
stream.WriteObjectField(`metric`)
|
stream.WriteObjectField(`metric`)
|
||||||
m, err := s.Metric.MarshalJSON()
|
marshalLabelsJSON(s.Metric, stream)
|
||||||
if err != nil {
|
|
||||||
stream.Error = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.SetBuffer(append(stream.Buffer(), m...))
|
|
||||||
stream.WriteMore()
|
stream.WriteMore()
|
||||||
if s.H == nil {
|
if s.H == nil {
|
||||||
stream.WriteObjectField(`value`)
|
stream.WriteObjectField(`value`)
|
||||||
|
@ -194,12 +186,7 @@ func marshalExemplarJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||||
|
|
||||||
// "labels" key.
|
// "labels" key.
|
||||||
stream.WriteObjectField(`labels`)
|
stream.WriteObjectField(`labels`)
|
||||||
lbls, err := p.Labels.MarshalJSON()
|
marshalLabelsJSON(p.Labels, stream)
|
||||||
if err != nil {
|
|
||||||
stream.Error = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream.SetBuffer(append(stream.Buffer(), lbls...))
|
|
||||||
|
|
||||||
// "value" key.
|
// "value" key.
|
||||||
stream.WriteMore()
|
stream.WriteMore()
|
||||||
|
@ -217,3 +204,28 @@ func marshalExemplarJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||||
func marshalExemplarJSONEmpty(unsafe.Pointer) bool {
|
func marshalExemplarJSONEmpty(unsafe.Pointer) bool {
|
||||||
return false
|
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