Recover stalled database connections and skip unreachable chat recipients
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-09-13 20:21:20 +09:00
parent 8094cbb8f7
commit d5a9c13b68
10 changed files with 122 additions and 14 deletions

View File

@@ -1,6 +1,8 @@
"""Health reflects a live event loop and a usable database connection."""
import asyncio
import logging
import os
import threading
import time
from pathlib import Path
@@ -13,9 +15,12 @@ HEARTBEAT = Path(os.getenv("HEARTBEAT_FILE", "/tmp/lottery-heartbeat"))
async def heartbeat():
while True:
async with async_session_maker() as session:
await session.execute(text("SELECT 1"))
HEARTBEAT.write_text(str(time.time()), encoding="ascii")
async with asyncio.timeout(20):
async with async_session_maker() as session:
await session.execute(text("SELECT 1"))
pending = HEARTBEAT.with_name(HEARTBEAT.name + ".tmp")
pending.write_text(str(time.time()), encoding="ascii")
pending.replace(HEARTBEAT)
await asyncio.sleep(10)
@@ -26,5 +31,20 @@ def healthy():
return False
def start_watchdog(grace_seconds=75, interval=10):
"""Exit even when the event loop or driver cancellation itself is stuck."""
stopped = threading.Event()
started = time.monotonic()
def watch():
while not stopped.wait(interval):
if time.monotonic() - started >= grace_seconds and not healthy():
logging.getLogger(__name__).critical("Heartbeat expired; restarting the application")
os._exit(1)
threading.Thread(target=watch, name="heartbeat-watchdog", daemon=True).start()
return stopped
if __name__ == "__main__":
raise SystemExit(0 if healthy() else 1)