Skip to content

Commit

Permalink
Merge pull request #440 from zenador/sparsehistograms
Browse files Browse the repository at this point in the history
Optimise JSON marshalling for sparse histograms
  • Loading branch information
codesome authored Feb 2, 2023
2 parents ca1f99b + 18a4214 commit f9c1994
Show file tree
Hide file tree
Showing 8 changed files with 469 additions and 320 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.17
require (
github.com/go-kit/log v0.2.1
github.com/golang/protobuf v1.5.2
github.com/json-iterator/go v1.1.12
github.com/julienschmidt/httprouter v1.3.0
github.com/matttproud/golang_protobuf_extensions v1.0.4
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
Expand All @@ -23,6 +24,8 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/stretchr/testify v1.8.0 // indirect
golang.org/x/sys v0.3.0 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
Expand All @@ -159,9 +160,11 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
Expand Down
26 changes: 4 additions & 22 deletions model/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,37 +100,19 @@ func (s Sample) MarshalJSON() ([]byte, error) {
return json.Marshal(&v)
}

type sampleHistogramPairPtr struct {
Timestamp Time
Histogram *SampleHistogram
}

func (s *sampleHistogramPairPtr) UnmarshalJSON(buf []byte) error {
tmp := []interface{}{&s.Timestamp, &s.Histogram}
wantLen := len(tmp)
if err := json.Unmarshal(buf, &tmp); err != nil {
return err
}
if gotLen := len(tmp); gotLen != wantLen {
return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen)
}
return nil
}

// UnmarshalJSON implements json.Unmarshaler.
// TODO: simplify and remove the need for both sampleHistogramPairPtr and SampleHistogramPair
func (s *Sample) UnmarshalJSON(b []byte) error {
v := struct {
Metric Metric `json:"metric"`
Value SamplePair `json:"value"`
Histogram sampleHistogramPairPtr `json:"histogram"`
Metric Metric `json:"metric"`
Value SamplePair `json:"value"`
Histogram SampleHistogramPair `json:"histogram"`
}{
Metric: s.Metric,
Value: SamplePair{
Timestamp: s.Timestamp,
Value: s.Value,
},
Histogram: sampleHistogramPairPtr{
Histogram: SampleHistogramPair{
Timestamp: s.Timestamp,
Histogram: s.Histogram,
},
Expand Down
28 changes: 18 additions & 10 deletions model/value_float.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,15 @@ import (
"fmt"
"math"
"strconv"
"unsafe"

jsoniter "github.com/json-iterator/go"
)

func init() {
jsoniter.RegisterTypeEncoderFunc("model.SamplePair", marshalSamplePairJSON, marshalJSONIsEmpty)
}

var (
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
// non-existing sample pair. It is a SamplePair with timestamp Earliest and
Expand Down Expand Up @@ -71,17 +78,18 @@ type SamplePair struct {
Value SampleValue
}

// MarshalJSON implements json.Marshaler.
// marshalSamplePairJSON writes `[ts, "val"]`.
func marshalSamplePairJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
p := *((*SamplePair)(ptr))
stream.WriteArrayStart()
MarshalTimestamp(int64(p.Timestamp), stream)
stream.WriteMore()
MarshalValue(float64(p.Value), stream)
stream.WriteArrayEnd()
}

func (s SamplePair) MarshalJSON() ([]byte, error) {
t, err := json.Marshal(s.Timestamp)
if err != nil {
return nil, err
}
v, err := json.Marshal(s.Value)
if err != nil {
return nil, err
}
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
return jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(s)
}

// UnmarshalJSON implements json.Unmarshaler.
Expand Down
80 changes: 47 additions & 33 deletions model/value_float_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,42 @@ import (
"testing"
)

var (
samplePairMatrixPlain = `[{"metric":{"__name__":"test_metric"},"values":[[1234.567,"123.1"],[12345.678,"123.12"]]},{"metric":{"foo":"bar"},"values":[[2234.567,"223.1"],[22345.678,"223.12"]]}]`
samplePairMatrixValue = Matrix{
&SampleStream{
Metric: Metric{
MetricNameLabel: "test_metric",
},
Values: []SamplePair{
{
Value: 123.1,
Timestamp: 1234567,
},
{
Value: 123.12,
Timestamp: 12345678,
},
},
},
&SampleStream{
Metric: Metric{
"foo": "bar",
},
Values: []SamplePair{
{
Value: 223.1,
Timestamp: 2234567,
},
{
Value: 223.12,
Timestamp: 22345678,
},
},
},
}
)

func TestEqualValues(t *testing.T) {
tests := map[string]struct {
in1, in2 SampleValue
Expand Down Expand Up @@ -231,39 +267,8 @@ func TestMatrixJSON(t *testing.T) {
value: Matrix{},
},
{
plain: `[{"metric":{"__name__":"test_metric"},"values":[[1234.567,"123.1"],[12345.678,"123.12"]]},{"metric":{"foo":"bar"},"values":[[2234.567,"223.1"],[22345.678,"223.12"]]}]`,
value: Matrix{
&SampleStream{
Metric: Metric{
MetricNameLabel: "test_metric",
},
Values: []SamplePair{
{
Value: 123.1,
Timestamp: 1234567,
},
{
Value: 123.12,
Timestamp: 12345678,
},
},
},
&SampleStream{
Metric: Metric{
"foo": "bar",
},
Values: []SamplePair{
{
Value: 223.1,
Timestamp: 2234567,
},
{
Value: 223.12,
Timestamp: 22345678,
},
},
},
},
plain: samplePairMatrixPlain,
value: samplePairMatrixValue,
},
}

Expand Down Expand Up @@ -291,3 +296,12 @@ func TestMatrixJSON(t *testing.T) {
}
}
}

func BenchmarkJSONMarshallingSamplePairMatrix(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := json.Marshal(samplePairMatrixValue)
if err != nil {
b.Fatal("error marshalling")
}
}
}
50 changes: 23 additions & 27 deletions model/value_histogram.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ import (
"fmt"
"strconv"
"strings"
"unsafe"

jsoniter "github.com/json-iterator/go"
)

func init() {
jsoniter.RegisterTypeEncoderFunc("model.HistogramBucket", marshalHistogramBucketJSON, marshalJSONIsEmpty)
jsoniter.RegisterTypeEncoderFunc("model.SampleHistogramPair", marshalSampleHistogramPairJSON, marshalJSONIsEmpty)
}

type FloatString float64

func (v FloatString) String() string {
Expand Down Expand Up @@ -49,24 +57,10 @@ type HistogramBucket struct {
Count FloatString
}

func (s HistogramBucket) MarshalJSON() ([]byte, error) {
b, err := json.Marshal(s.Boundaries)
if err != nil {
return nil, err
}
l, err := json.Marshal(s.Lower)
if err != nil {
return nil, err
}
u, err := json.Marshal(s.Upper)
if err != nil {
return nil, err
}
c, err := json.Marshal(s.Count)
if err != nil {
return nil, err
}
return []byte(fmt.Sprintf("[%s,%s,%s,%s]", b, l, u, c)), nil
// marshalHistogramBucketJSON writes fmt.Sprintf("[%s,%s,%s,%s]", b.Boundaries, b.Lower, b.Upper, b.Count).
func marshalHistogramBucketJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
b := *((*HistogramBucket)(ptr))
MarshalHistogramBucket(b, stream)
}

func (s *HistogramBucket) UnmarshalJSON(buf []byte) error {
Expand Down Expand Up @@ -139,19 +133,21 @@ type SampleHistogramPair struct {
Histogram *SampleHistogram
}

// marshalSampleHistogramPairJSON writes `[ts, "val"]`.
func marshalSampleHistogramPairJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
p := *((*SampleHistogramPair)(ptr))
stream.WriteArrayStart()
MarshalTimestamp(int64(p.Timestamp), stream)
stream.WriteMore()
MarshalHistogram(*p.Histogram, stream)
stream.WriteArrayEnd()
}

func (s SampleHistogramPair) MarshalJSON() ([]byte, error) {
t, err := json.Marshal(s.Timestamp)
if err != nil {
return nil, err
}
if s.Histogram == nil {
return nil, fmt.Errorf("histogram is nil")
}
v, err := json.Marshal(s.Histogram)
if err != nil {
return nil, err
}
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
return jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(s)
}

func (s *SampleHistogramPair) UnmarshalJSON(buf []byte) error {
Expand Down
Loading

0 comments on commit f9c1994

Please sign in to comment.