fix(backend): listen connections reconnect after a dropped connection or a postgres restart and catch up on what they missed

This commit is contained in:
hh committed 2026-10-19 02:29:19 +00:00
1 parent 17e27beb59
commit 3373c31a02
5 files changed
+129 -54

No files matched your search

+7 -15
View File
@@ -6,7 +6,7 @@ import asyncpg
import uvloop
from dependencies.container import container
from utils.env import env
from utils.listen import Listener
from utils.logging import logger, setup_logging
from utils.read.bots import BOTS_CHANGED_CHANNEL
from utils.storage import ContentAddressedStorage
@@ -24,19 +24,13 @@ async def _retry_loop(registry: BotRegistry) -> None:
await registry.sync()
async def _listen_changes(
registry: BotRegistry, tasks: set[asyncio.Task]
) -> asyncpg.Connection:
def on_change(
_conn: asyncpg.Connection, _pid: int, _channel: str, _payload: str
) -> None:
def _change_listener(registry: BotRegistry, tasks: set[asyncio.Task]) -> Listener:
def on_change(_payload: str) -> None:
task = asyncio.create_task(registry.sync())
tasks.add(task)
task.add_done_callback(tasks.discard)
conn = await asyncpg.connect(dsn=env.db.connection_url)
await conn.add_listener(BOTS_CHANGED_CHANNEL, on_change)
return conn
return Listener("watcher", {BOTS_CHANGED_CHANNEL: on_change}, lambda: on_change(""))
async def runner() -> None:
@@ -44,7 +38,7 @@ async def runner() -> None:
storage = await container.get(ContentAddressedStorage)
registry = BotRegistry(pool, storage)
tasks: set[asyncio.Task] = set()
listen_conn: asyncpg.Connection | None = None
listener = _change_listener(registry, tasks)
stop = asyncio.Event()
asyncio.get_running_loop().add_signal_handler(signal.SIGTERM, stop.set)
retry = asyncio.create_task(_retry_loop(registry))
@@ -52,16 +46,14 @@ async def runner() -> None:
await registry.sync()
if not registry.running:
logger.warning("[yellow]No bots yet. Add a bot token in the web UI.[/]")
listen_conn = await _listen_changes(registry, tasks)
await listener.start()
logger.info("[green]Bot watcher running.[/]")
await stop.wait()
finally:
retry.cancel()
with contextlib.suppress(asyncio.CancelledError):
await retry
if listen_conn is not None:
with contextlib.suppress(Exception):
await listen_conn.close()
await listener.stop()
await registry.close()
await container.close()