fix(realtime): wait for postgres_changes system ready before emitting SUBSCRIBED
Summary
Fixes a race condition where SUBSCRIBED status is emitted before PostgreSQL logical replication is ready,
causing early database writes to miss postgres_changes events.
Problem
The Realtime subscription has a two-stage backend process:
- Stage 1 (Fast): Phoenix WebSocket channel join completes
- Stage 2 (Slow): PostgreSQL replication slot creation and stream initialization
Previously, the client emitted SUBSCRIBED after Stage 1, but the replication stream wasn't ready until Stage 2 completed. Any database writes in this window (~1-3 seconds) would not trigger postgres_changes events.
Solution
Wait for the server's system message { extension: 'postgres_changes', status: 'ok' } before emitting SUBSCRIBED when postgres_changes bindings are present. This message is sent by the server after Realtime.Tenants.ReplicationConnection confirms the replication stream is active.
For channels without postgres_changes (broadcast/presence only), behavior is unchanged.
Changes
- Store subscribe callback and track pending system confirmations
- Add
_maybeEmitSubscribed()that only fires when all confirmations received - Handle system message in
_trigger()to clear pending state - Updated existing lifecycle tests to include system message trigger.
Related
- Closes: https://github.com/supabase/supabase-js/issues/1599
coverage: 81.014% (+0.02%) from 80.997% when pulling 17add66d41865d08788fbb1ff1b8b2f29d1c72ff on 7ttp:fix/realtime-subscription-race-condition into 09aa10628b00cbdf65ace0f9e8e79237e7c0c1dc on supabase:master.
@7ttp I know you and @edgurgel discussed this internally, so I am putting a "do not merge" label