fix(worker): survive startup race — wrap poll in try/except, add restart policy and backend healthcheck dependency

- Poll loop body is now wrapped in outer try/except so UndefinedTable (or any
  transient DB error) logs a warning and retries after poll_interval instead of
  killing the worker process.
- docker-compose: worker gets `restart: unless-stopped` so Docker recovers it
  automatically on any unexpected exit.
- docker-compose: worker `depends_on` now includes `backend: condition: service_healthy`
  so it waits for the backend (which runs migrations) to be ready before polling.

Fixes DAP-40.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Paperclip Agent
2026-06-13 20:13:32 +02:00
parent 281ce7f9e4
commit 6c7c63e5c8
2 changed files with 19 additions and 12 deletions

View File

@@ -38,6 +38,7 @@ services:
worker:
build: ./worker
restart: unless-stopped
environment:
DATABASE_URL: postgresql://kino:${DB_PASSWORD:-kino}@db:5432/kino
MEDIA_PATH: /media
@@ -48,6 +49,8 @@ services:
depends_on:
db:
condition: service_healthy
backend:
condition: service_healthy
frontend:
build: ./frontend

View File

@@ -86,18 +86,22 @@ def process_job(db: Session, dl: Download) -> None:
def run() -> None:
log.info("Worker started, polling every %ds", settings.poll_interval)
while True:
with Session(engine) as db:
pending = db.execute(
select(Download).where(Download.status == DownloadStatus.pending).limit(1)
).scalar_one_or_none()
if pending:
try:
process_job(db, pending)
except Exception as exc:
log.error("Job %d failed: %s", pending.id, exc)
pending.status = DownloadStatus.failed
pending.error = str(exc)
db.commit()
try:
with Session(engine) as db:
pending = db.execute(
select(Download).where(Download.status == DownloadStatus.pending).limit(1)
).scalar_one_or_none()
if pending:
try:
process_job(db, pending)
except Exception as exc:
log.error("Job %d failed: %s", pending.id, exc)
pending.status = DownloadStatus.failed
pending.error = str(exc)
db.commit()
except Exception as exc:
# Transient errors (e.g. table not yet created, DB not ready) — backoff and retry
log.warning("Poll error (will retry): %s", exc)
time.sleep(settings.poll_interval)