Remove all channels
Unsubscribe every Realtime channel and close the socket.
supabase.remove_all_channels walks the registry, calls channel.unsubscribe on each, clears the registry, and disconnects the socket. Idempotent — a follow-up call on an empty registry is a no-op.
Use this at process shutdown, in test teardown, or any time you want the realtime client to release its socket and background threads (heartbeat, reconnect, read-loop).
Signature
supabase.remove_all_channelsThe same method lives on the realtime sub-client (supabase.realtime.remove_all_channels) — the top-level shortcut forwards through dispatch_realtime.
Parameters
This method has no parameters.
Returns
In sync mode the call blocks until every channel's phx_leave is queued and the socket is closed, then returns the realtime client itself. Under async: true the realtime teardown runs in a child Async task and the call returns the Async::Task so the calling fiber doesn't stall on N blocking Socket#send writes. Call .wait on the task to await completion.
Example — shut everything down
supabase.channel("public:countries").on_postgres_changes("*", schema: "public", table: "countries") { |p| handle(p) }.subscribe
supabase.channel("public:orders").on_postgres_changes("INSERT", schema: "public", table: "orders") { |p| dispatch(p) }.subscribe
supabase.channel("room:42").on_broadcast("chat:message") { |p| log(p) }.subscribe
# ...later...
supabase.remove_all_channels
# All three go through LEAVING → CLOSED, socket disconnects, background threads stop.Example — clean teardown in a Rails initializer / on_exit
at_exit do
supabase.remove_all_channels
endExample — async: true returns an Async::Task
Under async: true the call returns an Async::Task. .wait on it inside a reactor.
supabase = Supabase.create_client(
supabase_url: ENV.fetch("SUPABASE_URL"),
supabase_key: ENV.fetch("SUPABASE_ANON_KEY"),
async: true
)
# ...subscribe to a few channels...
Async do
task = supabase.remove_all_channels
task.wait
endOutside a reactor, Async { } degrades to running inline — the call matches the sync path.
Iteration is over a snapshot
The implementation iterates @channels.dup so a channel that removes itself from the registry during its own unsubscribe (via handle_channel_close → _remove_channel) doesn't shift the live array mid-loop. After the loop, the registry is cleared and disconnect is called — an intentional close, not a bare @socket.close, so the background reconnect thread is stopped instead of immediately bringing the socket back up.
`remove_all_channels` returns `Async::Task` under `async: true`
The realtime client is thread-based and every channel's phx_leave is a blocking Socket#send; without dispatch, the calling fiber under async: true would hang while N writes drain serially. The umbrella's dispatch_realtime wraps the fan-out in an Async block so the call returns an Async::Task the caller .waits on. In sync mode (async: false, the default) the block runs straight through and the call returns self (the realtime client). Verified by spec/async/remove_channel_non_blocking_spec.rb.