fix(hub): reassign pending_owners on WS reconnect — prevents approval response loss
This commit is contained in:
69
hub.py
69
hub.py
@@ -269,6 +269,19 @@ class WSHub:
|
||||
self.project_connections[conn.project] = set()
|
||||
self.project_connections[conn.project].add(conn.conn_id)
|
||||
|
||||
# FIX: Reassign orphaned pending_owners (from dead conn_ids) to this new connection.
|
||||
# When Extension reconnects, old conn_id entries become stale.
|
||||
reassigned = 0
|
||||
for rid, cid in list(self.pending_owners.items()):
|
||||
if cid not in self.connections:
|
||||
self.pending_owners[rid] = conn.conn_id
|
||||
reassigned += 1
|
||||
if reassigned:
|
||||
logger.info(
|
||||
f"[HUB] Reassigned {reassigned} orphaned pending_owners "
|
||||
f"to new conn {conn.conn_id} (project={conn.project})"
|
||||
)
|
||||
|
||||
# Broadcast instance update to all project connections
|
||||
asyncio.create_task(self._broadcast_instance_update(conn.project))
|
||||
|
||||
@@ -292,10 +305,26 @@ class WSHub:
|
||||
if not self.project_connections[project]:
|
||||
del self.project_connections[project]
|
||||
|
||||
# Clean up pending ownership
|
||||
# Reassign pending ownership to another connection in same project
|
||||
# (instead of deleting — prevents approval responses from being lost)
|
||||
stale = [rid for rid, cid in self.pending_owners.items() if cid == conn_id]
|
||||
for rid in stale:
|
||||
del self.pending_owners[rid]
|
||||
if stale:
|
||||
remaining = self.project_connections.get(project, set()) - {conn_id}
|
||||
new_owner = None
|
||||
for cid in remaining:
|
||||
c = self.connections.get(cid)
|
||||
if c and c.authenticated:
|
||||
new_owner = cid
|
||||
break
|
||||
for rid in stale:
|
||||
if new_owner:
|
||||
self.pending_owners[rid] = new_owner
|
||||
logger.info(
|
||||
f"[HUB] Reassigned pending {rid[:12]} → {new_owner} "
|
||||
f"(disconnected {conn_id})"
|
||||
)
|
||||
else:
|
||||
del self.pending_owners[rid]
|
||||
|
||||
# Close WebSocket if still open
|
||||
if not conn.ws.closed:
|
||||
@@ -374,13 +403,39 @@ class WSHub:
|
||||
return False
|
||||
|
||||
async def send_response_to_pending_owner(self, request_id: str, message: dict):
|
||||
"""Route a response to the Extension that created the pending request."""
|
||||
"""Route a response to the Extension that created the pending request.
|
||||
|
||||
Falls back to any active connection in the same project if the
|
||||
original owner disconnected (e.g. Extension WS reconnected with
|
||||
a new conn_id).
|
||||
"""
|
||||
conn_id = self.pending_owners.get(request_id)
|
||||
if conn_id:
|
||||
await self.send_to_connection(conn_id, message)
|
||||
# Clean up after response delivered
|
||||
conn = self.connections.get(conn_id)
|
||||
if conn and conn.authenticated and not conn.ws.closed:
|
||||
await self.send_to_connection(conn_id, message)
|
||||
self.pending_owners.pop(request_id, None)
|
||||
return True
|
||||
# Original owner dead — try to find any active connection in same project
|
||||
project = conn.project if conn else None
|
||||
if project:
|
||||
for cid in self.project_connections.get(project, set()):
|
||||
c = self.connections.get(cid)
|
||||
if c and c.authenticated and not c.ws.closed:
|
||||
await self.send_to_connection(cid, message)
|
||||
self.pending_owners.pop(request_id, None)
|
||||
logger.info(
|
||||
f"[HUB] Rerouted response {request_id[:12]} → {cid} "
|
||||
f"(original {conn_id} dead)"
|
||||
)
|
||||
return True
|
||||
# No active connection found — clean up
|
||||
self.pending_owners.pop(request_id, None)
|
||||
return True
|
||||
logger.warning(
|
||||
f"[HUB] Response {request_id[:12]} lost: owner {conn_id} dead, "
|
||||
f"no active connections in project"
|
||||
)
|
||||
return False
|
||||
logger.warning(f"[HUB] No owner for pending {request_id[:12]}")
|
||||
return False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user