From eea5ec35181d3385d6f87fb2a69d6d417c8db967 Mon Sep 17 00:00:00 2001 From: Neil Twigg Date: Fri, 26 Sep 2025 13:44:55 +0100 Subject: [PATCH] Optimised meta snapshot encoding when `GOEXPERIMENT=jsonv2` enabled Signed-off-by: Neil Twigg --- scripts/runTestsOnTravis.sh | 2 ++ server/events.go | 2 +- server/jetstream_cluster.go | 6 ++++- server/jetstream_cluster_snap_v1.go | 24 ++++++++++++++++++++ server/jetstream_cluster_snap_v2.go | 35 +++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 server/jetstream_cluster_snap_v1.go create mode 100644 server/jetstream_cluster_snap_v2.go diff --git a/scripts/runTestsOnTravis.sh b/scripts/runTestsOnTravis.sh index 596f85e287f..36183defc35 100755 --- a/scripts/runTestsOnTravis.sh +++ b/scripts/runTestsOnTravis.sh @@ -2,6 +2,8 @@ set -ex +export GOEXPERIMENT=jsonv2 + if [ "$1" = "compile" ]; then # First check that NATS builds. go build -v; diff --git a/server/events.go b/server/events.go index ff2ee46367e..b979bd19945 100644 --- a/server/events.go +++ b/server/events.go @@ -315,7 +315,7 @@ type ClientInfo struct { Name string `json:"name,omitempty"` Lang string `json:"lang,omitempty"` Version string `json:"ver,omitempty"` - RTT time.Duration `json:"rtt,omitempty"` + RTT time.Duration `json:"rtt,omitempty,format:units"` Server string `json:"server,omitempty"` Cluster string `json:"cluster,omitempty"` Alternates []string `json:"alts,omitempty"` diff --git a/server/jetstream_cluster.go b/server/jetstream_cluster.go index 39064848109..079b89edb25 100644 --- a/server/jetstream_cluster.go +++ b/server/jetstream_cluster.go @@ -31,6 +31,7 @@ import ( "strings" "sync/atomic" "time" + "weak" "github.com/klauspost/compress/s2" "github.com/minio/highwayhash" @@ -69,6 +70,9 @@ type jetStreamCluster struct { peerStreamCancelMove *subscription // To pop out the monitorCluster before the raft layer. qch chan struct{} + // Meta snapshot encode buffers. Weakly held so GC can collect at any time, but + // can help us optimistically to reduce allocations on future snapshot calls. + lastsnap weak.Pointer[bytes.Buffer] // nolint:unused } // Used to track inflight stream add requests to properly re-use same group and sync subject. @@ -1584,7 +1588,7 @@ func (js *jetStream) metaSnapshot() ([]byte, error) { // Track how long it took to marshal the JSON mstart := time.Now() - b, err := json.Marshal(streams) + b, err := js.metaSnapshotJSON(streams) mend := time.Since(mstart) js.mu.RUnlock() diff --git a/server/jetstream_cluster_snap_v1.go b/server/jetstream_cluster_snap_v1.go new file mode 100644 index 00000000000..bf45b82233f --- /dev/null +++ b/server/jetstream_cluster_snap_v1.go @@ -0,0 +1,24 @@ +// Copyright 2025 The NATS 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. +// +//go:build !goexperiment.jsonv2 + +package server + +import ( + "encoding/json" +) + +func (js *jetStream) metaSnapshotJSON(streams []writeableStreamAssignment) ([]byte, error) { + return json.Marshal(streams) +} diff --git a/server/jetstream_cluster_snap_v2.go b/server/jetstream_cluster_snap_v2.go new file mode 100644 index 00000000000..b019a73bc3d --- /dev/null +++ b/server/jetstream_cluster_snap_v2.go @@ -0,0 +1,35 @@ +// Copyright 2025 The NATS 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. +// +//go:build goexperiment.jsonv2 + +package server + +import ( + "bytes" + jsonv2 "encoding/json/v2" + "weak" +) + +func (js *jetStream) metaSnapshotJSON(streams []writeableStreamAssignment) ([]byte, error) { + b := js.cluster.lastsnap.Value() + if b == nil { + b = bytes.NewBuffer(nil) + js.cluster.lastsnap = weak.Make(b) + } + b.Reset() + if err := jsonv2.MarshalWrite(b, streams); err != nil { + return nil, err + } + return b.Bytes(), nil +}