diff --git a/api/prometheus/v1/api.go b/api/prometheus/v1/api.go index 62f2ed7dc..99aa99813 100644 --- a/api/prometheus/v1/api.go +++ b/api/prometheus/v1/api.go @@ -17,6 +17,7 @@ package v1 import ( "context" + gojson "encoding/json" "errors" "fmt" "math" @@ -745,10 +746,10 @@ type Stat struct { func (rg *RuleGroup) UnmarshalJSON(b []byte) error { v := struct { - Name string `json:"name"` - File string `json:"file"` - Interval float64 `json:"interval"` - Rules []json.RawMessage `json:"rules"` + Name string `json:"name"` + File string `json:"file"` + Interval float64 `json:"interval"` + Rules []gojson.RawMessage `json:"rules"` }{} if err := json.Unmarshal(b, &v); err != nil { @@ -860,8 +861,8 @@ func (r *RecordingRule) UnmarshalJSON(b []byte) error { func (qr *queryResult) UnmarshalJSON(b []byte) error { v := struct { - Type model.ValueType `json:"resultType"` - Result json.RawMessage `json:"result"` + Type model.ValueType `json:"resultType"` + Result gojson.RawMessage `json:"result"` }{} err := json.Unmarshal(b, &v) @@ -1477,12 +1478,12 @@ type apiClientImpl struct { } type apiResponse struct { - Status string `json:"status"` - Data json.RawMessage `json:"data"` - ErrorType ErrorType `json:"errorType"` - Error string `json:"error"` - Warnings []string `json:"warnings,omitempty"` - Infos []string `json:"infos,omitempty"` + Status string `json:"status"` + Data gojson.RawMessage `json:"data"` + ErrorType ErrorType `json:"errorType"` + Error string `json:"error"` + Warnings []string `json:"warnings,omitempty"` + Infos []string `json:"infos,omitempty"` } func apiError(code int) bool { diff --git a/api/prometheus/v1/api_bench_test.go b/api/prometheus/v1/api_bench_test.go index 97f502e56..6450600e6 100644 --- a/api/prometheus/v1/api_bench_test.go +++ b/api/prometheus/v1/api_bench_test.go @@ -120,76 +120,126 @@ func BenchmarkSamplesJsonSerialization(b *testing.B) { } b.Run("marshal", func(b *testing.B) { - b.Run("encoding/json/floats", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - if _, err := json.Marshal(floats); err != nil { - b.Fatal(err) + b.Run("floats", func(b *testing.B) { + b.Run("json", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := json.Marshal(floats); err != nil { + b.Fatal(err) + } } + }) + if supportsJSONv2 { + b.Run("jsonv2", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jsonv2Marshal(floats); err != nil { + b.Fatal(err) + } + } + }) } - }) - b.Run("jsoniter/floats", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - if _, err := jsoniter.Marshal(floats); err != nil { - b.Fatal(err) + b.Run("jsoniter", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jsoniter.Marshal(floats); err != nil { + b.Fatal(err) + } } - } + }) }) - b.Run("encoding/json/histograms", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - if _, err := json.Marshal(histograms); err != nil { - b.Fatal(err) + b.Run("histograms", func(b *testing.B) { + b.Run("json", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := json.Marshal(histograms); err != nil { + b.Fatal(err) + } } + }) + if supportsJSONv2 { + b.Run("jsonv2", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jsonv2Marshal(histograms); err != nil { + b.Fatal(err) + } + } + }) } - }) - b.Run("jsoniter/histograms", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - if _, err := jsoniter.Marshal(histograms); err != nil { - b.Fatal(err) + b.Run("jsoniter", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jsoniter.Marshal(histograms); err != nil { + b.Fatal(err) + } } - } + }) }) }) b.Run("unmarshal", func(b *testing.B) { - b.Run("encoding/json/floats", func(b *testing.B) { - b.ReportAllocs() - var m model.Matrix - for i := 0; i < b.N; i++ { - if err := json.Unmarshal(floatBytes, &m); err != nil { - b.Fatal(err) + b.Run("floats", func(b *testing.B) { + b.Run("json", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var m model.Matrix + if err := json.Unmarshal(floatBytes, &m); err != nil { + b.Fatal(err) + } } + }) + if supportsJSONv2 { + b.Run("jsonv2", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var m model.Matrix + if err := jsonv2Unmarshal(floatBytes, &m); err != nil { + b.Fatal(err) + } + } + }) } - }) - b.Run("jsoniter/floats", func(b *testing.B) { - b.ReportAllocs() - var m model.Matrix - for i := 0; i < b.N; i++ { - if err := jsoniter.Unmarshal(floatBytes, &m); err != nil { - b.Fatal(err) + b.Run("jsoniter", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var m model.Matrix + if err := jsoniter.Unmarshal(floatBytes, &m); err != nil { + b.Fatal(err) + } } - } + }) }) - b.Run("encoding/json/histograms", func(b *testing.B) { - b.ReportAllocs() - var m model.Matrix - for i := 0; i < b.N; i++ { - if err := json.Unmarshal(histogramBytes, &m); err != nil { - b.Fatal(err) + b.Run("histograms", func(b *testing.B) { + b.Run("json", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var m model.Matrix + if err := json.Unmarshal(histogramBytes, &m); err != nil { + b.Fatal(err) + } } + }) + if supportsJSONv2 { + b.Run("jsonv2", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var m model.Matrix + if err := jsonv2Unmarshal(histogramBytes, &m); err != nil { + b.Fatal(err) + } + } + }) } - }) - b.Run("jsoniter/histograms", func(b *testing.B) { - b.ReportAllocs() - var m model.Matrix - for i := 0; i < b.N; i++ { - if err := jsoniter.Unmarshal(histogramBytes, &m); err != nil { - b.Fatal(err) + b.Run("jsoniter", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var m model.Matrix + if err := jsoniter.Unmarshal(histogramBytes, &m); err != nil { + b.Fatal(err) + } } - } + }) }) }) }) diff --git a/api/prometheus/v1/api_bench_test_127.go b/api/prometheus/v1/api_bench_test_127.go new file mode 100644 index 000000000..3add4d36c --- /dev/null +++ b/api/prometheus/v1/api_bench_test_127.go @@ -0,0 +1,26 @@ +//go:build go1.27 + +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1 + +import ( + jsonv2 "encoding/json/v2" +) + +const supportsJSONv2 = true + +var jsonv2Marshal = jsonv2.Marshal + +var jsonv2Unmarshal = jsonv2.Unmarshal diff --git a/api/prometheus/v1/api_bench_test_pre127.go b/api/prometheus/v1/api_bench_test_pre127.go new file mode 100644 index 000000000..88c552c26 --- /dev/null +++ b/api/prometheus/v1/api_bench_test_pre127.go @@ -0,0 +1,28 @@ +//go:build !go1.27 + +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1 + +import "errors" + +const supportsJSONv2 = false + +func jsonv2Marshal(v any) ([]byte, error) { + return nil, errors.New("unsupported") +} + +func jsonv2Unmarshal(data []byte, v any) error { + return errors.New("unsupported") +} diff --git a/api/prometheus/v1/api_test.go b/api/prometheus/v1/api_test.go index cae4eb67c..f187ad0e9 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -15,6 +15,7 @@ package v1 import ( "context" + gojson "encoding/json" "errors" "io" "math" @@ -1444,7 +1445,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusUnprocessableEntity, response: &apiResponse{ Status: "error", - Data: json.RawMessage(`null`), + Data: gojson.RawMessage(`null`), ErrorType: ErrBadData, Error: "failed", }, @@ -1458,7 +1459,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusUnprocessableEntity, response: &apiResponse{ Status: "error", - Data: json.RawMessage(`"test"`), + Data: gojson.RawMessage(`"test"`), ErrorType: ErrTimeout, Error: "timed out", }, @@ -1490,7 +1491,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusBadRequest, response: &apiResponse{ Status: "error", - Data: json.RawMessage(`null`), + Data: gojson.RawMessage(`null`), ErrorType: ErrBadData, Error: "end timestamp must not be before start time", }, @@ -1511,7 +1512,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusUnprocessableEntity, response: &apiResponse{ Status: "success", - Data: json.RawMessage(`"test"`), + Data: gojson.RawMessage(`"test"`), }, expectedErr: &Error{ Type: ErrBadResponse, @@ -1522,7 +1523,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusUnprocessableEntity, response: &apiResponse{ Status: "success", - Data: json.RawMessage(`"test"`), + Data: gojson.RawMessage(`"test"`), ErrorType: ErrTimeout, Error: "timed out", }, @@ -1535,7 +1536,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusOK, response: &apiResponse{ Status: "error", - Data: json.RawMessage(`"test"`), + Data: gojson.RawMessage(`"test"`), ErrorType: ErrTimeout, Error: "timed out", }, @@ -1548,7 +1549,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusOK, response: &apiResponse{ Status: "error", - Data: json.RawMessage(`"test"`), + Data: gojson.RawMessage(`"test"`), ErrorType: ErrTimeout, Error: "timed out", Warnings: []string{"a"}, @@ -1563,7 +1564,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusOK, response: &apiResponse{ Status: "error", - Data: json.RawMessage(`"test"`), + Data: gojson.RawMessage(`"test"`), ErrorType: ErrTimeout, Error: "timed out", Infos: []string{"b"}, @@ -1578,7 +1579,7 @@ func TestAPIClientDo(t *testing.T) { code: http.StatusOK, response: &apiResponse{ Status: "error", - Data: json.RawMessage(`"test"`), + Data: gojson.RawMessage(`"test"`), ErrorType: ErrTimeout, Error: "timed out", Warnings: []string{"a"},