Conversation
channel() always built a new AsyncRealtimeChannel and stored it in
socket.channels under its topic, replacing any channel already there.
Incoming messages are routed by topic, so a second channel("room") call
silently stopped delivering broadcasts, presence and postgres changes to
the first, already subscribed channel.
Return the existing channel for the topic unless it is being left,
matching realtime-js.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What kind of change does this PR introduce?
Bug fix (realtime).
What is the current behavior?
AsyncRealtimeClient.channel(topic)always creates a newAsyncRealtimeChanneland stores it withself.channels[topic] = chan. If a channel for that topic already exists, the new one replaces it._listenroutes every incoming message by topic (self.channels.get(message.topic)). So after a secondchannel("room")call, broadcasts, presence and postgres changes forrealtime:roomgo to the new channel, which is not subscribed and has no callbacks. The first channel is still joined on the server, but its callbacks never fire again, and nothing reports an error. Its later leave reply is routed to the new channel too, so the first channel also stays inLEAVING.Code ported from supabase-js is where this shows up. There,
supabase.channel('room')returns the existing channel for the topic, so calling it again, for example tosend_broadcastfrom a handler, is a common pattern.Reproduction on
main, with the socket's websocket mocked and a real broadcast frame fed through_listen:What is the new behavior?
channel()returns the existing channel for the topic, unless that channel is leaving. This matches realtime-jsRealtimeClient.channel(), which returns the channel it finds for the topic. Afterunsubscribe()(stateLEAVING), a new channel is still created, as before, so the unsubscribe-and-recreate flow is unchanged.Additional context
New tests in
tests/test_connection.py(mocked websocket, no server needed):test_channel_returns_existing_channel_for_same_topic: onmain, a secondchannel("room")returns a different object.test_second_channel_call_keeps_first_subscription_receiving: onmain,received == []. On this branch, it is[{"n": 1}].test_channel_after_unsubscribe_creates_new_channel: passes before and after. It guards the recreate-after-unsubscribe flow.Results:
main, the first two tests fail. On this branch, all three pass.src/realtime/testsrun without a local Supabase: the only failures are the 6 tests that need the server at127.0.0.1:54321. They fail the same way onmain.ruff check,ruff format --checkandmypy src/realtime testsare clean.I searched the open issues and PRs (topic, channel twice, duplicate channel) and found no overlap. #1652 and #1656 touch other parts of
client.py.This PR was prepared by an AI coding agent (Claude Code) working from the breken-ai account. The tests above were run as described.
🤖 Generated with Claude Code