From 54eafee30b83e644abd950355e70f468ee01b8db Mon Sep 17 00:00:00 2001 From: kshitij <82637225+kshitijk4poor@users.noreply.github.com> Date: Sun, 2 Aug 2026 21:55:31 +0530 Subject: [PATCH] refactor(gateway): single allocator for the shared routing counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-up on the #64169 salvage: _save_entry duplicated _snapshot_routing_locked's counter-bump line verbatim. The stale-write protection is a total order over ONE counter — extract _next_routing_generation_locked() so the two allocation sites can't drift apart silently. --- gateway/session.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/gateway/session.py b/gateway/session.py index c99b8a39cd..92e2a5ae09 100644 --- a/gateway/session.py +++ b/gateway/session.py @@ -1476,12 +1476,23 @@ class SessionStore: data, generation = self._snapshot_routing_locked() self._persist_routing_data(data, generation) + def _next_routing_generation_locked(self) -> int: + """Bump and return the shared routing counter. Caller holds ``_lock``. + + BOTH full snapshots (_snapshot_routing_locked) and single-entry fast + saves (_save_entry) MUST allocate from this one counter — the stale- + write protection in _persist_routing_data/_save_entry is a total order + over serialization times and silently breaks if the two paths ever + number themselves independently. + """ + self._routing_generation = getattr(self, "_routing_generation", 0) + 1 + return self._routing_generation + def _snapshot_routing_locked(self) -> tuple[Dict[str, Any], int]: """Capture immutable routing data and a monotonic generation.""" - self._routing_generation = getattr(self, "_routing_generation", 0) + 1 return ( {key: entry.to_dict() for key, entry in self._entries.items()}, - self._routing_generation, + self._next_routing_generation_locked(), ) def _persist_routing_data(self, data: Dict[str, Any], generation: int) -> None: @@ -1623,8 +1634,7 @@ class SessionStore: if entry is None: return entry_json = json.dumps(entry.to_dict()) - self._routing_generation = getattr(self, "_routing_generation", 0) + 1 - revision = self._routing_generation + revision = self._next_routing_generation_locked() _db = getattr(self, "_db", None) saver = getattr(_db, "save_gateway_routing_entry", None) if _db else None if callable(saver):