Skip to content

DO NOT MERGE - Disconnect shared state - #6942

Draft
benedikt-bartscher wants to merge 5 commits into
reflex-dev:mainfrom
benedikt-bartscher:disconnect-shared-state
Draft

DO NOT MERGE - Disconnect shared state#6942
benedikt-bartscher wants to merge 5 commits into
reflex-dev:mainfrom
benedikt-bartscher:disconnect-shared-state

Conversation

@benedikt-bartscher

@benedikt-bartscher benedikt-bartscher commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

based on #6934

Review in cubic

…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-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds cross-instance shared-state fan-out and delayed cleanup of subscriptions belonging to disconnected clients.

  • Adds a configurable reconnect grace period and delayed subscriber cleanup hook.
  • Resolves socket ownership through Redis before cross-worker fan-out.
  • Adds unit coverage for cross-instance connectivity and disconnect cleanup.

Confidence Score: 4/5

The 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

Important Files Changed

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

Comment thread reflex/istate/shared.py
await asyncio.sleep(grace)
if (event_namespace := app.event_namespace) is None:
return
if await event_namespace._token_manager.is_token_connected(token):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

@codspeed-hq

codspeed-hq Bot commented Aug 25, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 27 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing benedikt-bartscher:disconnect-shared-state (09d67ad) with main (f35b493)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant