DO NOT MERGE - Disconnect shared state - #6942
Conversation
…ce period Resolves the TODO in _do_update_other_tokens: tokens of clients that disconnected and did not reconnect within REFLEX_SHARED_STATE_DISCONNECT_GRACE (default 30s, 0 disables) are removed from the _linked_from subscriber sets of all shared states the client was linked to, so shared mutations stop fanning out modify_state churn for them. The new SharedState._on_subscriber_disconnected hook lets shared states clean up per-client data (e.g. presence bookkeeping). Safety: a reconnect within the grace (any instance, checked via is_token_connected) is a no-op; a client reaped too eagerly re-subscribes automatically on its next event through _internal_patch_linked_state. A new disconnect for the same token restarts the grace. Only the shared subscriber sets are touched, never the client's own _reflex_internal_links.
Greptile SummaryThis PR adds cross-instance shared-state fan-out and delayed cleanup of subscriptions belonging to disconnected clients.
Confidence Score: 4/5The reconnect/reap race must be fixed before merging because it can unsubscribe a currently connected client and suppress shared-state updates. The reap makes its connectivity decision before taking the shared-state lock, allowing a reconnect event to restore membership and then have that current membership removed without another connectivity check. Files Needing Attention: reflex/istate/shared.py
|
| Filename | Overview |
|---|---|
| reflex/istate/shared.py | Adds cross-instance fan-out and delayed subscription reaping, but the reap can remove a subscription restored by a concurrent reconnect. |
| reflex/utils/token_manager.py | Adds local and Redis-aware connectivity checks, including refreshing cached foreign socket records. |
| reflex/app.py | Schedules shared-state cleanup when a websocket disconnects, without canceling that task directly on reconnect. |
| packages/reflex-base/src/reflex_base/environment.py | Defines the configurable shared-state disconnect grace period with a 30-second default. |
| tests/units/istate/test_shared.py | Covers fan-out and basic reap behavior but omits the reconnect-after-connectivity-check race. |
Reviews (1): Last reviewed commit: "feat: unsubscribe disconnected clients f..." | Re-trigger Greptile
| await asyncio.sleep(grace) | ||
| if (event_namespace := app.event_namespace) is None: | ||
| return | ||
| if await event_namespace._token_manager.is_token_connected(token): |
There was a problem hiding this comment.
Reconnect subscription gets reaped
When a client reconnects after this connectivity check but before the reap acquires the shared-state lock, its next event restores _linked_from, and the pending reap then removes that current membership without rechecking connectivity, causing the connected client to miss shared-state updates until it sends another event.
Merging this PR will not alter performance
Comparing Footnotes
|
based on #6934