From d1e72ef0bcefd0c378f2448c6752e9e30a6ec860 Mon Sep 17 00:00:00 2001 From: NekoPunch Date: Sun, 2 Aug 2026 03:18:23 -0700 Subject: [PATCH] fix(atenet): make xDS snapshot versions restart-unique The in-memory version counter restarted at 1 on every router boot; if Envoy reconnected still holding an identical version string the snapshot cache saw a match and skipped the push, stranding Envoy on pre-restart config. Versions now carry a per-process epoch (unix seconds plus a random suffix) so no incarnation repeats an earlier one's strings, even across clock jumps. Fixes #617. --- cmd/atenet/internal/router/xds.go | 5 +++- cmd/atenet/internal/router/xds_test.go | 33 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/cmd/atenet/internal/router/xds.go b/cmd/atenet/internal/router/xds.go index 1685b6262..e2d76d8bf 100644 --- a/cmd/atenet/internal/router/xds.go +++ b/cmd/atenet/internal/router/xds.go @@ -16,6 +16,7 @@ package router import ( "context" + "crypto/rand" "fmt" "log/slog" "net" @@ -152,6 +153,7 @@ type XdsServer struct { snapshot cachev3.SnapshotCache srv serverv3.Server versionCount int64 + versionEpoch string mu sync.Mutex @@ -204,6 +206,7 @@ func NewXdsServer(xdsPort int) *XdsServer { xdsPort: xdsPort, snapshot: cache, srv: srv, + versionEpoch: strconv.FormatInt(time.Now().Unix(), 10) + "-" + rand.Text()[:8], extprocPort: 50051, // matches default extproc port extprocAddr: "127.0.0.1", ingressPort: 8080, @@ -408,7 +411,7 @@ func (x *XdsServer) UpdateSnapshot() error { defer x.mu.Unlock() x.versionCount++ - ver := strconv.FormatInt(x.versionCount, 10) + ver := x.versionEpoch + "-" + strconv.FormatInt(x.versionCount, 10) // connectEnabled is true when either CONNECT listener (plaintext or TLS) is // configured; the main_internal cluster/listener only exist to serve them. diff --git a/cmd/atenet/internal/router/xds_test.go b/cmd/atenet/internal/router/xds_test.go index 8580685cb..6fa5c428b 100644 --- a/cmd/atenet/internal/router/xds_test.go +++ b/cmd/atenet/internal/router/xds_test.go @@ -965,3 +965,36 @@ func TestXdsServer_BuildTracingRandomSamplingFromPolicy(t *testing.T) { }) } } + +func TestSnapshotVersionsUniqueAcrossRestarts(t *testing.T) { + deployAndGetVersion := func(t *testing.T, x *XdsServer) string { + t.Helper() + if err := x.UpdateSnapshot(); err != nil { + t.Fatalf("UpdateSnapshot: %v", err) + } + snap, err := x.snapshot.GetSnapshot(NodeID) + if err != nil { + t.Fatalf("GetSnapshot: %v", err) + } + return snap.GetVersion(resourcev3.ClusterType) + } + + seen := map[string]bool{} + first := NewXdsServer(0) + for range 3 { + v := deployAndGetVersion(t, first) + if seen[v] { + t.Fatalf("version %q minted twice by the same server", v) + } + seen[v] = true + } + + restarted := NewXdsServer(0) + for range 3 { + v := deployAndGetVersion(t, restarted) + if seen[v] { + t.Fatalf("version %q reused after restart; Envoy holding that version would not receive the new config", v) + } + seen[v] = true + } +}