feat: split celery workers, fix asyncpg-in-fork, add pipeline progress UI
Three overlapping fixes so the ingestion pipeline actually runs and the
user can see what it's doing:
Pipeline recovery
- app/database.py: use NullPool when MULITA_CELERY_WORKER=1 so each
Celery task opens a fresh asyncpg connection on its own event loop.
Fixes "another operation in progress" and "Future attached to a
different loop" errors that were dropping ~every thumbnail +
extract_metadata task on the floor.
- app/tasks/thumbs.py: initialize photo=None before the try and rollback
on error so a transport failure in the initial SELECT doesn't raise
UnboundLocalError in the except block and leak rows stuck in 'pending'.
- app/services/vision/bootstrap_models.py: on missing model files,
invoke export_models automatically instead of just warning. First
boot of a fresh install now self-heals.
- app/services/vision/export_models.py: shutil.move instead of
Path.rename so the YOLO export survives the /app → /data/models
cross-volume hop.
- requirements.txt: add ultralytics so export works in a stock image.
Worker topology
- docker-compose.yml: replace the single worker with worker-light
(default/high/low queues, c=2, IO-bound) and worker-vision (vision
queue, c=5, OMP_NUM_THREADS=1 to avoid oversubscription on 6 cores).
Vision is pinned to ≤5 parallel inferences so ONNX doesn't each
spawn an all-cores intra-op pool.
- .env / .env.example: CELERYD_CONCURRENCY replaced with
CELERY_LIGHT_CONCURRENCY + CELERY_VISION_CONCURRENCY.
- Backfill queries in thumbs / scan / vision now ORDER BY taken_at
DESC NULLS LAST so newest photos finish first — the library fills
in top-down in the UI instead of arbitrary insertion order.
Settings visibility
- routers/library.py: new GET /maintenance/pipeline-stats returning
done/total per stage (thumbnails, exif, gps, phash, embeddings,
tags, ocr, faces, face clusters, duplicate groups). Worker-status
now also reports the `vision` queue depth, which was missing.
- services/api.ts: PipelineStats / PipelineStage / ScanStatus types
and the matching client call.
- components/dialogs/SettingsDialog.tsx:
- new Pipeline Progress card with one progress bar per stage
- inline scan banner (processed/total/current folder) inside the
Library section while a scan is running
- Tasks/min throughput computed by diffing worker processed counters
between polls
- Workers section calls out the vision queue and documents the
CELERY_LIGHT/VISION_CONCURRENCY + docker compose up -d scale path
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
26
.env.example
26
.env.example
@@ -65,11 +65,27 @@ TZ=UTC
|
||||
|
||||
|
||||
# ── WORKER CONCURRENCY ───────────────────────────────────────────────────────
|
||||
|
||||
# How many parallel Celery worker processes to spin up. Each one can run
|
||||
# one scan / thumbnail / metadata job at a time. Bump on a beefy host with a
|
||||
# big library; lower on a Pi.
|
||||
CELERYD_CONCURRENCY=4
|
||||
#
|
||||
# The ingestion pipeline runs on two Celery worker services with separate
|
||||
# concurrency knobs so heavy vision tasks can't starve cheap IO tasks:
|
||||
#
|
||||
# worker-light (default / high / low queues)
|
||||
# Runs: scan, thumbnails, EXIF, pHash, duplicate regrouping.
|
||||
# Mostly IO-bound — 2 prefork children keep a library streaming in.
|
||||
#
|
||||
# worker-vision (vision queue)
|
||||
# Runs: embeddings, object detection, OCR, face extraction, content
|
||||
# classification. Each prefork child loads ~2 GB of ONNX model weights,
|
||||
# so set this to roughly (physical_cores − 1) and watch RAM.
|
||||
#
|
||||
# Defaults target a ~6 core / 16 GB host. Raise these, then
|
||||
# docker compose up -d worker-light worker-vision
|
||||
# to pick them up. Lower for a Pi; go higher on a workstation.
|
||||
#
|
||||
# The old `CELERYD_CONCURRENCY=N` single-worker variable is no longer
|
||||
# read — delete it from your .env if it's set.
|
||||
CELERY_LIGHT_CONCURRENCY=2
|
||||
CELERY_VISION_CONCURRENCY=5
|
||||
|
||||
|
||||
# ── INTERNAL (rarely overridden) ─────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user