Skip to content

max_concurrent_users is leaking when leaving channels #2233

Description

@mandar1045

Issue

When a client unsubscribes from a channel, RealtimeChannel.terminate/2 doesn't call UsersCounter.remove. This means the user stays in the census until the idle socket sweeper (Tracker) eventually kills the connection, which can take up to 10 minutes.

I found this while debugging an SPA where users frequently switch between channels. The connected user count kept increasing because users weren't being removed when they left a room, eventually causing "Too many connected users" errors.

Consequences

  1. Unnecessary Postgres connections: Ghost users can keep tenant Postgres pools alive for up to 10 minutes, potentially wasting connections.
  2. Customer friction: Stale users can consume max_concurrent_users slots, causing customers to hit their limits sooner and trigger rate limiting.

To Reproduce

  1. Open a WebSocket without joining a channel — uses 0 user slots.
  2. Join a channel — UsersCounter.add/2 is called, using 1 slot.
  3. Call channel.unsubscribe() — RealtimeChannel.terminate/2 runs.
  4. Bug: UsersCounter.remove is never called. The user has no active channels but still counts as 1 user for up to 10 minutes.

Proposed Fix

Remove the user from UsersCounter when their active channel count drops to 0 in terminate/2:

if Tracker.untrack(transport_pid) <= 0 do Realtime.UsersCounter.remove(transport_pid, tenant_id) end

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions