You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Presence diff stops processing leaves after an untracked key #1630
AsyncRealtimePresence._sync_diff uses break where it means continue. When a presence_diff message contains leaves for a key the client is not currently tracking, the loop aborts and every remaining key in that diff is silently ignored — those presences stay in presence.state forever and on_presence_leave never fires for them.
forkey, left_presencesinleaves.items():
current_presences=self.state.get(key, [])
iflen(current_presences) ==0:
break# aborts the whole loop; should skip just this key
Phoenix's presence.js — which this file is a port of — returns from the per-key callback there, i.e. continue.
Since leaves comes straight off the wire in _on_diff_event, the server decides both the contents and the key order. A diff that reports an untracked key before a tracked one is enough to lose the tracked leave.
Reproduction
fromrealtime._async.presenceimportAsyncRealtimePresencepresence=AsyncRealtimePresence()
presence.on_leave(lambdakey, current, left: print("on_leave", key))
presence._on_state_event({"bob": {"metas": [{"phx_ref": "ref-bob"}]}})
print("state:", presence.state)
# alice is unknown to this client (it joined after she left / never saw her state)presence._on_diff_event(
{
"joins": {},
"leaves": {
"alice": {"metas": [{"phx_ref": "ref-alice"}]},
"bob": {"metas": [{"phx_ref": "ref-bob"}]},
},
}
)
print("state:", presence.state)
Expected behavior
state: {'bob': [{'presence_ref': 'ref-bob'}]}
on_leave bob
state: {}
The unknown key is skipped and bob's leave is still processed.
Activity
fix(realtime): continue processing presence leaves (supabase#1630)
test(realtime): cover untracked presence leave (supabase#1630)