Skip to content

presence: count doc/snapshot listeners toward a room's emptiness - #485

Merged
TheGreatAxios merged 2 commits into
cl-7205-room-registry-destroyfrom
cl-7204-destroy-listener-emptiness
Aug 30, 2026
Merged

TheGreatAxios merged 2 commits into
cl-7205-room-registry-destroyfrom
cl-7204-destroy-listener-emptiness

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Stacked on #484 (CL-7205)

This branch is stacked on cl-7205-room-registry-destroy (CL-7205, PR #484) — it targets that branch, not main, because both fix the same destroyRoomIfEmpty function in packages/presence/src/room-registry.ts. Merge #484 first; this PR's diff (once #484 lands) will be just the two commits below.

Summary

Fixes CL-7204 (Medium). destroyRoomIfEmpty's emptiness check only looked at clientIdByPrincipal and listeners, ignoring docListeners and snapshotListeners — both populated through the registry's own public API (subscribeDocUpdates, subscribeSnapshots) independently of subscribe/join. A room kept alive only by one of those two listener sets was destroyed and silently recreated on the next otherwise-empty join/leave cycle, permanently orphaning that listener (it's invoked by the destroyed room's old Set, or never again).

What changed

  • isRoomEmpty (introduced in CL-7205) now also checks docListeners.size === 0 and snapshotListeners.size === 0.
  • subscribeDocUpdates and subscribeSnapshots's returned unsubscribe callbacks now also call destroyRoomIfEmpty — previously only subscribe's unsubscribe and leave/sweepStale did. Without this, broadening the emptiness check would introduce a new leak: a room kept alive only by a doc/snapshot listener would never get cleaned up once that listener goes away.

Test plan

  • WORKBENCH_CHECK_SINCE=origin/main bun run typecheck — clean across all 6 affected packages
  • WORKBENCH_CHECK_SINCE=origin/main bun run test — all green
  • bun run lint — clean
  • bun run check:structural — clean

destroyRoomIfEmpty's emptiness check only looks at clientIdByPrincipal
and listeners, so a room kept alive only by a subscribeDocUpdates or
subscribeSnapshots listener is destroyed and silently recreated on the
next empty join/leave cycle, permanently orphaning that listener.
destroyRoomIfEmpty now treats a room as non-empty while it still has a
subscribeDocUpdates or subscribeSnapshots listener, not just presence
members and subscribe() listeners, and both of those subscriptions'
teardown paths now attempt cleanup themselves so a room kept alive only
by one of them still gets torn down once that listener goes away.
@TheGreatAxios
TheGreatAxios merged commit a2a788b into main Aug 30, 2026
5 checks passed
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