-
-
Notifications
You must be signed in to change notification settings - Fork 391
Open
Open
Copy link
Labels
bugSomething isn't workingSomething isn't working
Description
Describe the bug
After a random numbers of hours (or days), subscription to broadcast get lost and heartbeat doesn't get replies anymore without any subscription error raised. Once this happens, the channel doesn't receive broadcast events anymore.
2025-11-04 10:34:26,436 - INFO - Attempting to connect to WebSocket at wss://xxx.supabase.co/realtime/v1/websocket?apikey=sb_secret_xxx
2025-11-04 10:34:26,538 - INFO - WebSocket connection established successfully
2025-11-04 10:34:26,571 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 10:34:26,576 - INFO - receive: '{"ref":null,"event":"phx_reply","payload":{"status":"ok","response":{}},"topic":"phoenix"}'
2025-11-04 10:34:26,576 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='phoenix' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=None)) ref=None
2025-11-04 10:34:26,740 - INFO - send: {"event":"phx_join","payload":{"config":{"broadcast":null,"presence":null,"private":true,"postgres_changes":[]},"access_token":"sb_secret_xxx"},"topic":"r>
2025-11-04 10:34:26,756 - INFO - receive: '{"ref":"1","event":"phx_reply","payload":{"status":"ok","response":{"postgres_changes":[]}},"topic":"realtime:test"}'
2025-11-04 10:34:26,757 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='realtime:test' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=[])) ref='1'
2025-11-04 10:34:26,757 - INFO - realtime:test : event=<ChannelEvents.reply: 'phx_reply'> topic='realtime:test' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=[])) ref='1'
2025-11-04 10:34:26,757 - WARNING - subscription status=SUBSCRIBED err=None
2025-11-04 10:34:26,757 - INFO - receive: '{"ref":null,"event":"presence_state","payload":{},"topic":"realtime:test"}'
2025-11-04 10:34:26,757 - INFO - parsed message as event=<ChannelEvents.presence_state: 'presence_state'> topic='realtime:test' payload={} ref=None
2025-11-04 10:34:26,757 - INFO - realtime:test : event=<ChannelEvents.presence_state: 'presence_state'> topic='realtime:test' payload={} ref=None
2025-11-04 10:34:36,742 - WARNING - subscription status=TIMED_OUT err=None
2025-11-04 10:34:51,573 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 10:34:51,578 - INFO - receive: '{"ref":null,"event":"phx_reply","payload":{"status":"ok","response":{}},"topic":"phoenix"}'
2025-11-04 10:34:51,578 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='phoenix' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=None)) ref=None
2025-11-04 10:35:16,575 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 10:35:16,580 - INFO - receive: '{"ref":null,"event":"phx_reply","payload":{"status":"ok","response":{}},"topic":"phoenix"}'
2025-11-04 10:35:16,581 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='phoenix' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=None)) ref=None
2025-11-04 10:35:41,577 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 10:35:41,583 - INFO - receive: '{"ref":null,"event":"phx_reply","payload":{"status":"ok","response":{}},"topic":"phoenix"}'
2025-11-04 10:35:41,583 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='phoenix' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=None)) ref=None
2025-11-04 10:36:06,579 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 10:36:06,590 - INFO - receive: '{"ref":null,"event":"phx_reply","payload":{"status":"ok","response":{}},"topic":"phoenix"}'
2025-11-04 10:36:06,590 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='phoenix' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=None)) ref=None
2025-11-04 10:36:31,582 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 10:36:31,589 - INFO - receive: '{"ref":null,"event":"phx_reply","payload":{"status":"ok","response":{}},"topic":"phoenix"}'
2025-11-04 10:36:31,590 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='phoenix' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=None)) ref=None
...
2025-11-04 13:51:07,387 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:51:07,413 - INFO - receive: '{"ref":null,"event":"phx_reply","payload":{"status":"ok","response":{}},"topic":"phoenix"}'
2025-11-04 13:51:07,413 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='phoenix' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=None)) ref=None
2025-11-04 13:51:32,389 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:51:32,398 - INFO - receive: '{"ref":null,"event":"phx_reply","payload":{"status":"ok","response":{}},"topic":"phoenix"}'
2025-11-04 13:51:32,398 - INFO - parsed message as event=<ChannelEvents.reply: 'phx_reply'> topic='phoenix' payload=SuccessReplyMessage(status='ok', response=ReplyPostgresChanges(postgres_changes=None)) ref=None
2025-11-04 13:51:57,391 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:52:22,392 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:52:47,394 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:53:12,395 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:53:37,397 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:54:02,398 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:54:27,400 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:54:52,401 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:55:17,403 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:55:42,404 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:56:07,406 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:56:32,407 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:56:57,407 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:57:22,409 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:57:47,410 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:58:12,411 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:58:37,413 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:59:02,416 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:59:27,418 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 13:59:52,421 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:00:17,424 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:00:42,426 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:01:07,428 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:01:32,430 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:01:57,432 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:02:22,433 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:02:47,435 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:03:12,437 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:03:37,438 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:04:02,440 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:04:27,442 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:04:52,444 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
2025-11-04 14:05:17,445 - INFO - send: {"event":"heartbeat","payload":{},"topic":"phoenix","ref":null,"join_ref":null}
...
The heartbeart code does not seem to check that a reply is received:
| async def _heartbeat(self) -> None: |
And there is no heartbeat callback to allow users to detect the issue:
| elif isinstance(message, HeartbeatMessage): # do nothing |
Reproduction
#!/usr/bin/env python3
import os, asyncio, logging
from supabase.client import AsyncClient
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
async def main():
def on_subscribe(status, err):
logging.warning(f"subscription status={status} err={err}")
def on_message(msg):
logging.info(msg)
client = AsyncClient(os.environ.get("SUPABASE_URL"), os.environ.get("SUPABASE_KEY"))
channel = client.channel("test", {"config": {"private": True}})
await channel.on_broadcast("UPDATE", on_message).subscribe(on_subscribe)
while True:
await asyncio.sleep(1)
asyncio.run(main())Steps to reproduce
Let the reproduction snippet above run until the issue happens.
It can take minutes, hours or days.
Library affected
realtime
Library version
supabase 2.22.4
Python version
python 3.12.3
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working