fix(vitalsharing): don't evict subscribers on transient send failures

_broadcast_share_to_plugin_clients was discarding a character from
_vital_sharing_subscribers whenever a single send_json hit the 1-second
timeout or raised any exception. Under heavy load this permanently
dropped clients from the subscriber set even though their WebSocket was
still fully connected — the user had to toggle vital sharing off and on
to get peer updates flowing again.

Now we log the send failure but leave the subscriber intact. Actual
eviction still happens on real WebSocket disconnect via the finally
block in the plugin receive loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-11 15:14:37 +02:00
parent 2b521d1df5
commit 7a31469d69

13
main.py
View file

@ -2419,10 +2419,16 @@ _vital_sharing_peer_state: Dict[str, dict] = {}
async def _broadcast_share_to_plugin_clients(data: dict, origin: str) -> None:
"""Forward a share_* message to all opted-in plugin clients except origin."""
"""Forward a share_* message to all opted-in plugin clients except origin.
Transient send failures are logged but do NOT evict the subscriber
eviction only happens on actual WebSocket disconnect (handled in the
plugin receive loop's finally block). Evicting aggressively here caused
a bug where a single slow send would silently drop a subscriber and
force the user to retoggle vital sharing to get peer updates again.
"""
if not _vital_sharing_subscribers:
return
stale: list[str] = []
for char_name, ws in list(plugin_conns.items()):
if char_name == origin:
continue
@ -2432,9 +2438,6 @@ async def _broadcast_share_to_plugin_clients(data: dict, origin: str) -> None:
await asyncio.wait_for(ws.send_json(data), timeout=1.0)
except Exception as e:
logger.debug(f"Failed forwarding share_* to {char_name}: {e}")
stale.append(char_name)
for name in stale:
_vital_sharing_subscribers.discard(name)
def _update_vital_sharing_peer_state(msg_type: str, data: dict) -> None: