From db37be902ec396437c90782ad56992be50d6e0b9 Mon Sep 17 00:00:00 2001 From: dtoro Date: Wed, 8 Apr 2026 12:01:19 +0200 Subject: [PATCH] fix: disable folder watcher on startup to unblock scan_folder Diagnosis: every backend restart was dispatching watch_folders.delay() unconditionally. watch_folders is an infinite-loop celery task (for changes in watch(*paths)). With CELERYD_CONCURRENCY=4 and several restarts during dev, all four worker slots ended up pinned by stale watch_folders instances, leaving zero workers free for scan_folder. The result: clicking "Scan all folders" successfully queued a task that then sat in the queue forever, the new /photos/sub folder was never walked, and the user's newly added photo never appeared. The watcher was only opportunistically useful and the user already triggers scans manually. Disabling it removes the foot-gun. Re- enabling needs: - a Redis lock so only one watcher runs at a time - or a dedicated long-running container with concurrency=1 - or a celery beat schedule with a singleton flag Until then, manual scans work. Cleared the backlog by wiping the redis broker volume so the stale watch_folders tasks are gone. Verified: post-fix, scan_folder runs in 0.12s and reports "Processed 7/7 files. Errors: 0", picking up the previously missing /photos/sub/Samuel_Colman... file. Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/app/services/scanner.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/backend/app/services/scanner.py b/backend/app/services/scanner.py index f55ac25..3aa9b07 100644 --- a/backend/app/services/scanner.py +++ b/backend/app/services/scanner.py @@ -8,7 +8,7 @@ from sqlalchemy import select from app.database import AsyncSessionLocal from app.models import SourceRoot -from app.tasks.scan import scan_all_source_roots, watch_folders +from app.tasks.scan import scan_all_source_roots from app.config import settings logger = logging.getLogger(__name__) @@ -49,15 +49,17 @@ async def bootstrap_default_source_root() -> None: async def start_initial_scan(): - """Start the initial library scan""" + """Start the initial library scan. + + NOTE: the folder watcher (watch_folders task) is intentionally NOT + dispatched here. It's an infinite loop celery task and every backend + restart was queuing a new instance, eventually pinning every worker + and starving scan_folder dispatches. Re-enabling it needs a Redis + lock or a dedicated long-running container — until then the user + triggers scans manually via "Scan all folders". + """ try: - # Queue scan of all source roots scan_all_source_roots.delay() - - # Start folder watcher if configured - if settings.scanner.watch: - watch_folders.delay() - logger.info("Initial scan queued successfully") except Exception as e: logger.error(f"Failed to start initial scan: {e}")