Skip to content

Commit 3917cca

Browse files
authored
fix(spanner): remove json-iterator dependency (#10099)
json-iterator heavily relies on an unmaintained modern-go/reflect2 that pokes at the internals of Go runtime. This was shown to cause failures in golang/go#54766 (comment). Switching to encoding/json does come with a performance penalty. Updates #9380
1 parent a0c097c commit 3917cca

File tree

5 files changed

+24
-44
lines changed

5 files changed

+24
-44
lines changed

spanner/go.mod

-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ require (
99
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0
1010
github.com/google/go-cmp v0.6.0
1111
github.com/googleapis/gax-go/v2 v2.12.3
12-
github.com/json-iterator/go v1.1.12
1312
go.opencensus.io v0.24.0
1413
go.opentelemetry.io/otel v1.24.0
1514
go.opentelemetry.io/otel/metric v1.24.0
@@ -39,8 +38,6 @@ require (
3938
github.com/golang/protobuf v1.5.4 // indirect
4039
github.com/google/s2a-go v0.1.7 // indirect
4140
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
42-
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
43-
github.com/modern-go/reflect2 v1.0.2 // indirect
4441
github.com/stretchr/testify v1.9.0 // indirect
4542
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
4643
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect

spanner/go.sum

-9
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,6 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
760760
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
761761
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
762762
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
763-
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
764763
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
765764
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
766765
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -817,8 +816,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
817816
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
818817
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
819818
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
820-
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
821-
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
822819
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
823820
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
824821
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
@@ -843,11 +840,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
843840
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
844841
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
845842
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
846-
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
847-
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
848-
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
849-
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
850-
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
851843
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
852844
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
853845
github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
@@ -877,7 +869,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
877869
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
878870
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
879871
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
880-
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
881872
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
882873
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
883874
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

spanner/test/opentelemetry/test/go.mod

-3
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ require (
3737
github.com/google/s2a-go v0.1.7 // indirect
3838
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
3939
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
40-
github.com/json-iterator/go v1.1.12 // indirect
41-
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
42-
github.com/modern-go/reflect2 v1.0.2 // indirect
4340
go.opencensus.io v0.24.0 // indirect
4441
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
4542
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect

spanner/test/opentelemetry/test/go.sum

-9
Original file line numberDiff line numberDiff line change
@@ -1531,7 +1531,6 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
15311531
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
15321532
github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
15331533
github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
1534-
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
15351534
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
15361535
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
15371536
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
@@ -1587,8 +1586,6 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo
15871586
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
15881587
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
15891588
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
1590-
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
1591-
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
15921589
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
15931590
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
15941591
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
@@ -1625,11 +1622,6 @@ github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S
16251622
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
16261623
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
16271624
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
1628-
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
1629-
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
1630-
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
1631-
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
1632-
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
16331625
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
16341626
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
16351627
github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
@@ -1665,7 +1657,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
16651657
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
16661658
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
16671659
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
1668-
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
16691660
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
16701661
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
16711662
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

spanner/value.go

+24-20
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"database/sql"
2222
"database/sql/driver"
2323
"encoding/base64"
24+
"encoding/json"
2425
"fmt"
2526
"math"
2627
"math/big"
@@ -32,7 +33,6 @@ import (
3233
"cloud.google.com/go/civil"
3334
"cloud.google.com/go/internal/fields"
3435
sppb "cloud.google.com/go/spanner/apiv1/spannerpb"
35-
jsoniter "github.com/json-iterator/go"
3636
"google.golang.org/grpc/codes"
3737
"google.golang.org/protobuf/proto"
3838
proto3 "google.golang.org/protobuf/types/known/structpb"
@@ -114,7 +114,7 @@ var (
114114

115115
jsonNullBytes = []byte("null")
116116

117-
jsonProvider = jsoniter.ConfigCompatibleWithStandardLibrary
117+
jsonUseNumber bool
118118
)
119119

120120
// UseNumberWithJSONDecoderEncoder specifies whether Cloud Spanner JSON numbers are decoded
@@ -124,11 +124,15 @@ var (
124124
// NOTE 1: Calling this method affects the behavior of all clients created by this library, both existing and future instances.
125125
// NOTE 2: This method sets a global variable that is used by the client to encode/decode JSON numbers. Access to the global variable is not synchronized. You should only call this method when there are no goroutines encoding/decoding Cloud Spanner JSON values. It is recommended to only call this method during the initialization of your application, and preferably before you create any Cloud Spanner clients, and/or in tests when there are no queries being executed.
126126
func UseNumberWithJSONDecoderEncoder(useNumber bool) {
127-
jsonProvider = jsoniter.Config{
128-
EscapeHTML: true,
129-
SortMapKeys: true, // Sort map keys to ensure deterministic output, to be consistent with encoding.
130-
UseNumber: useNumber,
131-
}.Froze()
127+
jsonUseNumber = useNumber
128+
}
129+
130+
func jsonUnmarshal(data []byte, v any) error {
131+
dec := json.NewDecoder(bytes.NewReader(data))
132+
if jsonUseNumber {
133+
dec.UseNumber()
134+
}
135+
return dec.Decode(v)
132136
}
133137

134138
// Encoder is the interface implemented by a custom type that can be encoded to
@@ -297,7 +301,7 @@ func (n *NullString) UnmarshalJSON(payload []byte) error {
297301
return nil
298302
}
299303
var s *string
300-
if err := jsonProvider.Unmarshal(payload, &s); err != nil {
304+
if err := jsonUnmarshal(payload, &s); err != nil {
301305
return err
302306
}
303307
if s != nil {
@@ -866,7 +870,7 @@ func (n NullJSON) String() string {
866870
if !n.Valid {
867871
return nullString
868872
}
869-
b, err := jsonProvider.Marshal(n.Value)
873+
b, err := json.Marshal(n.Value)
870874
if err != nil {
871875
return fmt.Sprintf("error: %v", err)
872876
}
@@ -888,7 +892,7 @@ func (n *NullJSON) UnmarshalJSON(payload []byte) error {
888892
return nil
889893
}
890894
var v interface{}
891-
err := jsonProvider.Unmarshal(payload, &v)
895+
err := jsonUnmarshal(payload, &v)
892896
if err != nil {
893897
return fmt.Errorf("payload cannot be converted to a struct: got %v, err: %w", string(payload), err)
894898
}
@@ -972,7 +976,7 @@ func (n PGJsonB) String() string {
972976
if !n.Valid {
973977
return nullString
974978
}
975-
b, err := jsonProvider.Marshal(n.Value)
979+
b, err := json.Marshal(n.Value)
976980
if err != nil {
977981
return fmt.Sprintf("error: %v", err)
978982
}
@@ -994,7 +998,7 @@ func (n *PGJsonB) UnmarshalJSON(payload []byte) error {
994998
return nil
995999
}
9961000
var v interface{}
997-
err := jsonProvider.Unmarshal(payload, &v)
1001+
err := jsonUnmarshal(payload, &v)
9981002
if err != nil {
9991003
return fmt.Errorf("payload cannot be converted to a struct: got %v, err: %w", string(payload), err)
10001004
}
@@ -1007,7 +1011,7 @@ func nulljson(valid bool, v interface{}) ([]byte, error) {
10071011
if !valid {
10081012
return jsonNullBytes, nil
10091013
}
1010-
return jsonProvider.Marshal(v)
1014+
return json.Marshal(v)
10111015
}
10121016

10131017
// GenericColumnValue represents the generic encoded value and type of the
@@ -1714,7 +1718,7 @@ func decodeValue(v *proto3.Value, t *sppb.Type, ptr interface{}, opts ...DecodeO
17141718
}
17151719
x := v.GetStringValue()
17161720
var y interface{}
1717-
err := jsonProvider.Unmarshal([]byte(x), &y)
1721+
err := jsonUnmarshal([]byte(x), &y)
17181722
if err != nil {
17191723
return err
17201724
}
@@ -1873,7 +1877,7 @@ func decodeValue(v *proto3.Value, t *sppb.Type, ptr interface{}, opts ...DecodeO
18731877
}
18741878
x := v.GetStringValue()
18751879
var y interface{}
1876-
err := jsonProvider.Unmarshal([]byte(x), &y)
1880+
err := jsonUnmarshal([]byte(x), &y)
18771881
if err != nil {
18781882
return err
18791883
}
@@ -2566,7 +2570,7 @@ func (dsc decodableSpannerType) decodeValueToCustomType(v *proto3.Value, t *sppb
25662570
}
25672571
x := v.GetStringValue()
25682572
var y interface{}
2569-
err := jsonProvider.Unmarshal([]byte(x), &y)
2573+
err := jsonUnmarshal([]byte(x), &y)
25702574
if err != nil {
25712575
return err
25722576
}
@@ -2581,7 +2585,7 @@ func (dsc decodableSpannerType) decodeValueToCustomType(v *proto3.Value, t *sppb
25812585
}
25822586
x := v.GetStringValue()
25832587
var y interface{}
2584-
err := jsonProvider.Unmarshal([]byte(x), &y)
2588+
err := jsonUnmarshal([]byte(x), &y)
25852589
if err != nil {
25862590
return err
25872591
}
@@ -3272,7 +3276,7 @@ func decodeNullJSONArrayToNullJSON(pb *proto3.ListValue) (*NullJSON, error) {
32723276
}
32733277
s := fmt.Sprintf("[%s]", strings.Join(strs, ","))
32743278
var y interface{}
3275-
err := jsonProvider.Unmarshal([]byte(s), &y)
3279+
err := jsonUnmarshal([]byte(s), &y)
32763280
if err != nil {
32773281
return nil, err
32783282
}
@@ -3929,7 +3933,7 @@ func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) {
39293933
pt = listType(pgNumericType())
39303934
case NullJSON:
39313935
if v.Valid {
3932-
b, err := jsonProvider.Marshal(v.Value)
3936+
b, err := json.Marshal(v.Value)
39333937
if err != nil {
39343938
return nil, nil, err
39353939
}
@@ -3946,7 +3950,7 @@ func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) {
39463950
pt = listType(jsonType())
39473951
case PGJsonB:
39483952
if v.Valid {
3949-
b, err := jsonProvider.Marshal(v.Value)
3953+
b, err := json.Marshal(v.Value)
39503954
if err != nil {
39513955
return nil, nil, err
39523956
}

0 commit comments

Comments
 (0)