fix(download): release DB connections across the gallery-dl subprocess #60
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to the timeout-ladder fix (already on main). Backfill events were still stranding empty — but the worker logs showed the timeout salvage working (
Download timeout for anduo/patreon after 1170.0s (18 files written)), so this was a second, downstream bug.Root cause
DownloadServiceheld the async + sync DB connections checked out across the entire (≤19.5-min backfill) gallery-dl subprocess. The server reaped the idle connection, so phase 3's first query died withasyncpg ConnectionDoesNotExistError. ThatDBAPIErrortrippeddownload_source'sautoretry_for; the retry re-entered_phase1_setup, hit the in-flight guard (event stillrunning), returnedin_flightand no-opped in ~0.15s — leaving the event to strand empty for the recovery sweep.pool_pre_pingwas already on but can't validate a held connection (only pool checkouts).Changes
DownloadService.download_sourcecloses the async + sync sessions after phase 1, before the subprocess, so phase 3 re-acquires a live connection (makes the class's "Phase 2 — no DB connection" docstring true).NullPoolso the phase-3 reconnect always opens a fresh socket, not a reaped pooled one.Tests
test_releases_db_connections_before_subprocess(release happens beforegdl.download, event still finalizes) +test_async_task_engine_uses_nullpool. All existingtest_download_service.pytests still pass. CI green on dev HEAD576e16d.🤖 Generated with Claude Code
Backfill events were STILL stranding empty after the timeout-ladder fix. Worker logs showed the salvage path working ("Download timeout for anduo/patreon after 1170.0s (18 files written)") but then: Retry in 3s: DBAPIError(ConnectionDoesNotExistError: connection was closed in the middle of operation) ...succeeded in 0.149s <- in-flight guard no-op Root cause: DownloadService held the async + sync DB connections checked out across the entire (≤19.5-min backfill) gallery-dl subprocess. The server reaps the idle connection, so phase 3's first query hits a dead socket. That DBAPIError trips download_source's autoretry_for, the retry re-enters _phase1_setup, sees the event still 'running', returns in_flight and no-ops — leaving the event to be stranded empty by the recovery sweep. pool_pre_ping was already on both engines but can't help a *held* connection (it only validates on pool checkout). Fix: - DownloadService.download_source closes the async + sync sessions after phase 1, before the subprocess, so phase 3 re-acquires a live connection (matches the class's "Phase 2 — no DB connection" docstring). - The per-task async engine switches to NullPool so phase 3 always opens a fresh connection rather than a pooled one the server may have reaped. Tests: assert connections are released before gdl.download runs and the event still finalizes; assert the task engine uses NullPool. Also fixes a stale 1800s->1170s comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>