Skip to content

Commit cab3aa9

Browse files
committed
Use "jsonv2"/go-json-experimental to marshal OpenAPI v2
Performance boost is also pretty impressive: ``` name old time/op new time/op delta SwaggerSpec_ExperimentalMarshal/json-8 102ms ± 1% 25ms ± 1% -75.12% (p=0.016 n=4+5) name old alloc/op new alloc/op delta SwaggerSpec_ExperimentalMarshal/json-8 52.4MB ± 4% 19.9MB ± 2% -62.02% (p=0.008 n=5+5) name old allocs/op new allocs/op delta SwaggerSpec_ExperimentalMarshal/json-8 210k ± 0% 76k ± 0% -63.82% (p=0.008 n=5+5) ``` Mostly because this removes the need to deserialize json into buffers that are then thrown away when the jsons are concatenated together (due to lots of embedded objects).
1 parent 4233a11 commit cab3aa9

31 files changed

+603
-52
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require (
2525
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c
2626
k8s.io/klog/v2 v2.2.0
2727
k8s.io/utils v0.0.0-20210802155522-efc7438f0176
28+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd
2829
sigs.k8s.io/structured-merge-diff/v4 v4.2.3
2930
sigs.k8s.io/yaml v1.2.0
3031
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
180180
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
181181
k8s.io/utils v0.0.0-20210802155522-efc7438f0176 h1:Mx0aa+SUAcNRQbs5jUzV8lkDlGFU8laZsY9jrcVX5SY=
182182
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
183+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
184+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
183185
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
184186
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
185187
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=

pkg/aggregator/aggregator_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type DebugSpec struct {
3535
}
3636

3737
func (d DebugSpec) String() string {
38-
bytes, err := json.MarshalIndent(d.Swagger, "", " ")
38+
bytes, err := d.Swagger.MarshalJSON()
3939
if err != nil {
4040
return fmt.Sprintf("DebugSpec.String failed: %s", err)
4141
}
@@ -1762,7 +1762,7 @@ func BenchmarkMergeSpecsIgnorePathConflictsWithKubeSpec(b *testing.B) {
17621762
}
17631763
}
17641764

1765-
specBytes, _ := json.Marshal(sp)
1765+
specBytes, _ := sp.MarshalJSON()
17661766
handler.ToProtoBinary(specBytes)
17671767

17681768
b.StopTimer()
@@ -2003,7 +2003,7 @@ func TestCloneSpec(t *testing.T) {
20032003
}
20042004

20052005
func cloneSpec(source *spec.Swagger) (*spec.Swagger, error) {
2006-
bytes, err := json.Marshal(source)
2006+
bytes, err := source.MarshalJSON()
20072007
if err != nil {
20082008
return nil, err
20092009
}

pkg/builder/openapi_test.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/emicklei/go-restful/v3"
2727
"github.com/stretchr/testify/assert"
2828
openapi "k8s.io/kube-openapi/pkg/common"
29+
"k8s.io/kube-openapi/pkg/util/jsontesting"
2930
"k8s.io/kube-openapi/pkg/validation/spec"
3031
)
3132

@@ -467,15 +468,17 @@ func TestBuildOpenAPISpec(t *testing.T) {
467468
if !assert.NoError(err) {
468469
return
469470
}
470-
expected_json, err := json.Marshal(expected)
471+
expected_json, err := expected.MarshalJSON()
471472
if !assert.NoError(err) {
472473
return
473474
}
474-
actual_json, err := json.Marshal(swagger)
475+
actual_json, err := swagger.MarshalJSON()
475476
if !assert.NoError(err) {
476477
return
477478
}
478-
assert.Equal(string(expected_json), string(actual_json))
479+
if err := jsontesting.JsonCompare(expected_json, actual_json); err != nil {
480+
t.Error(err)
481+
}
479482
}
480483

481484
func TestBuildOpenAPIDefinitionsForResource(t *testing.T) {
@@ -495,7 +498,9 @@ func TestBuildOpenAPIDefinitionsForResource(t *testing.T) {
495498
if !assert.NoError(err) {
496499
return
497500
}
498-
assert.Equal(string(expected_json), string(actual_json))
501+
if err := jsontesting.JsonCompare(expected_json, actual_json); err != nil {
502+
t.Error(err)
503+
}
499504
}
500505

501506
func TestBuildOpenAPIDefinitionsForResourceWithExtensionV2Schema(t *testing.T) {

pkg/builder3/openapi_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
openapi "k8s.io/kube-openapi/pkg/common"
3030
"k8s.io/kube-openapi/pkg/spec3"
31+
"k8s.io/kube-openapi/pkg/util/jsontesting"
3132
"k8s.io/kube-openapi/pkg/validation/spec"
3233
)
3334

@@ -462,5 +463,7 @@ func TestBuildOpenAPISpec(t *testing.T) {
462463
if !assert.NoError(err) {
463464
return
464465
}
465-
assert.Equal(string(expected_json), string(actual_json))
466+
if err := jsontesting.JsonCompare(expected_json, actual_json); err != nil {
467+
t.Error(err)
468+
}
466469
}

pkg/handler/handler.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package handler
1919
import (
2020
"bytes"
2121
"crypto/sha512"
22-
"encoding/json"
2322
"fmt"
2423
"net/http"
2524
"strconv"
@@ -100,7 +99,7 @@ func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
10099
o.rwMutex.Lock()
101100
defer o.rwMutex.Unlock()
102101
o.jsonCache = o.jsonCache.New(func() ([]byte, error) {
103-
return json.Marshal(openapiSpec)
102+
return openapiSpec.MarshalJSON()
104103
})
105104
o.protoCache = o.protoCache.New(func() ([]byte, error) {
106105
json, err := o.jsonCache.Get()

pkg/handler/handler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestRegisterOpenAPIVersionedService(t *testing.T) {
2626
t.Errorf("Unexpected error in unmarshalling SwaggerJSON: %v", err)
2727
}
2828

29-
returnedJSON, err := json.Marshal(s)
29+
returnedJSON, err := s.MarshalJSON()
3030
if err != nil {
3131
t.Errorf("Unexpected error in preparing returnedJSON: %v", err)
3232
}

pkg/internal/flags.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ package internal
1919
// Used by tests to selectively disable experimental JSON unmarshaler
2020
var UseOptimizedJSONUnmarshaling bool = true
2121
var UseOptimizedJSONUnmarshalingV3 bool = false
22+
23+
// Used by tests to selectively disable experimental JSON marshaler
24+
var UseOptimizedJSONMarshaling bool = true

pkg/internal/serialization.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,15 @@ package internal
1818

1919
import (
2020
"github.com/go-openapi/jsonreference"
21+
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
2122
)
2223

24+
// DeterministicMarshal calls the jsonv2 library with the deterministic
25+
// flag in order to have stable marshaling.
26+
func DeterministicMarshal(in any) ([]byte, error) {
27+
return jsonv2.MarshalOptions{Deterministic: true}.Marshal(jsonv2.EncodeOptions{}, in)
28+
}
29+
2330
// JSONRefFromMap populates a json reference object if the map v contains a $ref key.
2431
func JSONRefFromMap(jsonRef *jsonreference.Ref, v map[string]interface{}) error {
2532
if v == nil {

pkg/openapiconv/convert_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"testing"
2525

2626
"k8s.io/kube-openapi/pkg/spec3"
27+
"k8s.io/kube-openapi/pkg/util/jsontesting"
2728
"k8s.io/kube-openapi/pkg/validation/spec"
2829
)
2930

@@ -51,19 +52,19 @@ func TestConvert(t *testing.T) {
5152
t.Fatal(err)
5253
}
5354

54-
openAPIV2JSONBeforeConversion, err := json.Marshal(swaggerSpec)
55+
openAPIV2JSONBeforeConversion, err := swaggerSpec.MarshalJSON()
5556
if err != nil {
5657
t.Fatal(err)
5758
}
5859

5960
convertedV3Spec := ConvertV2ToV3(&swaggerSpec)
6061

61-
openAPIV2JSONAfterConversion, err := json.Marshal(swaggerSpec)
62+
openAPIV2JSONAfterConversion, err := swaggerSpec.MarshalJSON()
6263
if err != nil {
6364
t.Fatal(err)
6465
}
65-
if !reflect.DeepEqual(openAPIV2JSONBeforeConversion, openAPIV2JSONAfterConversion) {
66-
t.Errorf("Expected OpenAPI V2 to be untouched before and after conversion")
66+
if err := jsontesting.JsonCompare(openAPIV2JSONBeforeConversion, openAPIV2JSONAfterConversion); err != nil {
67+
t.Errorf("Expected OpenAPI V2 to be untouched before and after conversion: %v", err)
6768
}
6869

6970
spec3JSON, err := os.ReadFile(filepath.Join("testdata_generated_from_k8s/v3_" + tc.groupVersion + ".json"))

0 commit comments

Comments
 (0)