Skip to content

Conversation

@7ttp
Copy link
Contributor

@7ttp 7ttp commented Jan 17, 2026

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:

  1. Stage 1 (Fast): Phoenix WebSocket channel join completes
  2. 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

@7ttp 7ttp requested review from a team as code owners January 17, 2026 21:03
@coveralls
Copy link

Coverage Status

coverage: 81.014% (+0.02%) from 80.997%
when pulling 17add66 on 7ttp:fix/realtime-subscription-race-condition
into 09aa106 on supabase:master.

@mandarini
Copy link
Contributor

@7ttp I know you and @edgurgel discussed this internally, so I am putting a "do not merge" label

@mandarini mandarini added the do-not-merge Do not merge this PR. label Jan 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge Do not merge this PR.

3 participants