Skip to content

Commit e35caa6

Browse files
authored
[+] switch to json-iterator/go from encoding/json (#776)
Replaced standard "encoding/json" with "github.com/json-iterator/go" to improve serialization/deserialization speed and reduce memory allocations. This change is expected to lower CPU usage and heap pressure in high-throughput scenarios. Behavior remains compatible with the standard library.
1 parent b82ae3b commit e35caa6

File tree

16 files changed

+45
-29
lines changed

16 files changed

+45
-29
lines changed

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/gorilla/websocket v1.5.3
99
github.com/jackc/pgx/v5 v5.7.5
1010
github.com/jessevdk/go-flags v1.6.1
11+
github.com/json-iterator/go v1.1.12
1112
github.com/pashagolub/pgxmock/v4 v4.7.0
1213
github.com/prometheus/client_golang v1.22.0
1314
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
@@ -70,6 +71,8 @@ require (
7071
github.com/moby/sys/user v0.4.0 // indirect
7172
github.com/moby/sys/userns v0.1.0 // indirect
7273
github.com/moby/term v0.5.2 // indirect
74+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
75+
github.com/modern-go/reflect2 v1.0.2 // indirect
7376
github.com/morikuni/aec v1.0.0 // indirect
7477
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
7578
github.com/opencontainers/go-digest v1.0.0 // indirect

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl76
6666
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
6767
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
6868
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
69+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
6970
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
7071
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
7172
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -89,6 +90,8 @@ github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bB
8990
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
9091
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
9192
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
93+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
94+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
9295
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
9396
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
9497
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
@@ -123,6 +126,11 @@ github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g
123126
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
124127
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
125128
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
129+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
130+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
131+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
132+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
133+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
126134
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
127135
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
128136
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=

internal/db/conn.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package db
22

33
import (
44
"context"
5-
"encoding/json"
65
"reflect"
76

7+
jsoniter "github.com/json-iterator/go"
8+
89
pgx "github.com/jackc/pgx/v5"
910
pgconn "github.com/jackc/pgx/v5/pgconn"
1011
pgxpool "github.com/jackc/pgx/v5/pgxpool"
@@ -57,7 +58,7 @@ func MarshallParamToJSONB(v any) any {
5758
return nil
5859
}
5960
}
60-
if b, err := json.Marshal(v); err == nil {
61+
if b, err := jsoniter.ConfigFastest.Marshal(v); err == nil {
6162
return string(b)
6263
}
6364
return nil

internal/reaper/reaper.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,7 @@ func (r *Reaper) FetchMetric(ctx context.Context, md *sources.SourceConn, metric
490490
data[0]["is_up"] = 0 // should be updated if the "instance_up" metric definition is changed
491491
goto send_to_storageChannel
492492
}
493-
l.
494-
WithFields(map[string]any{"source": md.Name, "metric": metricName}).
495-
WithError(err).Error("failed to fetch metrics")
493+
l.WithError(err).Error("failed to fetch metrics")
496494

497495
return nil, err
498496
}

internal/sinks/cmdopts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import "time"
55
// CmdOpts specifies the storage configuration to store metrics measurements
66
type CmdOpts struct {
77
Sinks []string `long:"sink" mapstructure:"sink" description:"URI where metrics will be stored, can be used multiple times" env:"PW_SINK"`
8-
BatchingDelay time.Duration `long:"batching-delay" mapstructure:"batching-delay" description:"Max milliseconds to wait for a batched metrics flush" default:"250ms" env:"PW_BATCHING_DELAY"`
8+
BatchingDelay time.Duration `long:"batching-delay" mapstructure:"batching-delay" description:"Timeout to wait for a batched metrics flush" default:"950ms" env:"PW_BATCHING_DELAY"`
99
Retention int `long:"retention" mapstructure:"retention" description:"If set, metrics older than that will be deleted" default:"14" env:"PW_RETENTION"`
1010
RealDbnameField string `long:"real-dbname-field" mapstructure:"real-dbname-field" description:"Tag key for real database name" env:"PW_REAL_DBNAME_FIELD" default:"real_dbname"`
1111
SystemIdentifierField string `long:"system-identifier-field" mapstructure:"system-identifier-field" description:"Tag key for system identifier value" env:"PW_SYSTEM_IDENTIFIER_FIELD" default:"sys_id"`

internal/sinks/json.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package sinks
22

33
import (
44
"context"
5-
"encoding/json"
65
"time"
76

7+
jsoniter "github.com/json-iterator/go"
8+
89
"github.com/cybertec-postgresql/pgwatch/v3/internal/log"
910
"github.com/cybertec-postgresql/pgwatch/v3/internal/metrics"
1011
"gopkg.in/natefinch/lumberjack.v2"
@@ -17,7 +18,7 @@ import (
1718
type JSONWriter struct {
1819
ctx context.Context
1920
lw *lumberjack.Logger
20-
enc *json.Encoder
21+
enc *jsoniter.Encoder
2122
}
2223

2324
func NewJSONWriter(ctx context.Context, fname string) (*JSONWriter, error) {
@@ -27,7 +28,7 @@ func NewJSONWriter(ctx context.Context, fname string) (*JSONWriter, error) {
2728
ctx: ctx,
2829
lw: &lumberjack.Logger{Filename: fname, Compress: true},
2930
}
30-
jw.enc = json.NewEncoder(jw.lw)
31+
jw.enc = jsoniter.ConfigFastest.NewEncoder(jw.lw)
3132
go jw.watchCtx()
3233
return jw, nil
3334
}

internal/sinks/json_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package sinks
22

33
import (
44
"context"
5-
"encoding/json"
65
"os"
76
"testing"
87

8+
jsoniter "github.com/json-iterator/go"
9+
910
"github.com/cybertec-postgresql/pgwatch/v3/internal/metrics"
1011
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/require"
@@ -42,7 +43,7 @@ func TestJSONWriter_Write(t *testing.T) {
4243
var data map[string]any
4344
file, err := os.ReadFile(tempFile)
4445
r.NoError(err)
45-
err = json.Unmarshal(file, &data)
46+
err = jsoniter.ConfigFastest.Unmarshal(file, &data)
4647
r.NoError(err)
4748
a.Equal(msg.MetricName, data["metric"])
4849
a.Equal(len(msg.Data), len(data["data"].([]any)))

internal/sources/resolver.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import (
99
"context"
1010
"crypto/tls"
1111
"crypto/x509"
12-
"encoding/json"
1312
"errors"
1413
"fmt"
1514
"os"
1615
"path"
1716
"strings"
1817
"time"
1918

19+
jsoniter "github.com/json-iterator/go"
20+
2021
"github.com/cybertec-postgresql/pgwatch/v3/internal/db"
2122
"github.com/cybertec-postgresql/pgwatch/v3/internal/log"
2223
pgx "github.com/jackc/pgx/v5"
@@ -75,7 +76,7 @@ func jsonTextToStringMap(jsonText string) (map[string]string, error) {
7576
return retmap, nil
7677
}
7778
var iMap map[string]any
78-
if err := json.Unmarshal([]byte(jsonText), &iMap); err != nil {
79+
if err := jsoniter.ConfigFastest.Unmarshal([]byte(jsonText), &iMap); err != nil {
7980
return nil, err
8081
}
8182
for k, v := range iMap {

internal/webserver/jwt.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package webserver
22

33
import (
4-
"encoding/json"
54
"errors"
65
"fmt"
76
"net/http"
87
"time"
98

9+
jsoniter "github.com/json-iterator/go"
10+
1011
"github.com/golang-jwt/jwt/v5"
1112
)
1213

@@ -35,7 +36,7 @@ func (Server *WebUIServer) handleLogin(w http.ResponseWriter, r *http.Request) {
3536

3637
switch r.Method {
3738
case "POST":
38-
if err = json.NewDecoder(r.Body).Decode(&lr); err != nil {
39+
if err = jsoniter.ConfigFastest.NewDecoder(r.Body).Decode(&lr); err != nil {
3940
return
4041
}
4142
if !Server.IsCorrectPassword(lr) {

internal/webserver/jwt_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ package webserver
22

33
import (
44
"bytes"
5-
"encoding/json"
65
"io"
76
"net/http"
87
"net/http/httptest"
98
"testing"
109
"time"
1110

1211
"github.com/golang-jwt/jwt/v5"
12+
jsoniter "github.com/json-iterator/go"
1313
"github.com/stretchr/testify/assert"
1414
)
1515

16+
var json = jsoniter.ConfigFastest
17+
1618
func TestIsCorrectPassword(t *testing.T) {
1719
ts := &WebUIServer{CmdOpts: CmdOpts{WebUser: "user", WebPassword: "pass"}}
1820
assert.True(t, ts.IsCorrectPassword(loginReq{Username: "user", Password: "pass"}))

0 commit comments

Comments
 (0)