Skip to content

fix: workers stuck, images not processing after restart, dashboard stats frozen#22

Open
vonhex wants to merge 4 commits intoSpaceinvaderOne:mainfrom
vonhex:fix/processing-workers
Open

fix: workers stuck, images not processing after restart, dashboard stats frozen#22
vonhex wants to merge 4 commits intoSpaceinvaderOne:mainfrom
vonhex:fix/processing-workers

Conversation

@vonhex
Copy link
Copy Markdown

@vonhex vonhex commented May 1, 2026

Summary

Three bugs that cause images to never process and the dashboard to show stale data:

  • Scan crash on missing/slow mount — `_scan()` returned a 2-tuple `(0, 0)` but all callers unpack 3 values `(new_count, skipped, new_ids)`. If `/photos` isn't ready at startup (slow network mount), this threw a silent `ValueError` that killed the initial scan entirely.

  • Pending images stuck after restart — On restart the worker queue starts empty. The scanner skips files already in the DB (`skip_processed=True`), so any images that were `pending` or mid-flight (`processing`) from a previous session are stranded forever. Fixed by resetting `processing → pending` on startup and re-enqueuing all `pending` rows before the watcher starts.

  • Dashboard frozen at "Loading..." / "Calculating..." — In `updateDashboard()`, `w.stop_requested` was referenced before `const w = data.worker || {}` was declared, throwing a `ReferenceError` silently swallowed by `.catch(() => {})`. The worker bar never left "Loading..." and the progress text never left "Calculating...". Also adds a Resume Processing button wired to a new `POST /api/scan/resume` endpoint so users can restart after stopping without an app restart.

Test plan

  • Start app with photos directory unmounted — confirm no crash in logs
  • Add images, start processing, restart app mid-flight — confirm images resume automatically
  • Open dashboard — confirm worker status shows real values and progress text updates
  • Click Stop Processing — confirm button changes to "Resume Processing"
  • Click Resume Processing — confirm pending images are re-enqueued and processing restarts

🤖 Generated with Claude Code

vonhex and others added 3 commits May 1, 2026 12:40
- watcher: fix _scan() returning 2-tuple instead of 3-tuple when photos
  dir is missing — caused ValueError crash on every initial scan if the
  mount wasn't ready, silently killing image discovery
- main: on startup, reset processing→pending and re-enqueue all pending
  images so images stranded from a previous session aren't stuck forever
- worker: add resize() to gracefully stop/restart workers with a new count
- settings: call worker.resize() when concurrent_workers changes so the
  live pool count actually updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ulating, stop/resume flow

- app.js: move `const w = data.worker || {}` before its first use — it was
  referenced at line 175 but defined at line 227, causing a ReferenceError
  that silently killed the entire updateDashboard callback (worker status
  stayed 'Loading...', progress stayed 'Calculating...')
- app.js: replace the disabled 'Stopping...' button state with an enabled
  'Resume Processing' button so users can restart after stopping without
  needing a page reload or app restart
- scan.py: add POST /api/scan/resume — clears stop_requested and re-enqueues
  all pending images so processing can continue after a stop

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a one-click way to re-queue every failed image without having to
select them manually page by page.

- POST /api/images/retry-all-errors — resets all error images to pending
  (clears error_message) and enqueues them all in one DB query
- Queue template: Retry All Errors button shown alongside Retry Selected
  when the error filter is active
- app.js: retryAllErrors() function that calls the endpoint and reloads
  the page with a count toast

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vonhex
Copy link
Copy Markdown
Author

vonhex commented May 1, 2026

also made a retry app button for errors so you dont have to go page by page to requeue

…adaptive polling

- database: enable SQLite WAL mode + NORMAL sync + 32MB cache — readers
  and writers no longer block each other; eliminates the main source of
  UI stalls when the worker is actively processing images
- thumbnails: move PIL thumbnail generation into asyncio.to_thread() so
  CPU/IO-bound image work no longer blocks the event loop and stalls
  every other concurrent request
- images: thumbnail endpoint now fetches only file_path (not SELECT *)
  and returns Cache-Control: max-age=86400, immutable so the browser
  caches thumbnails for 24h instead of re-fetching on every page visit
- dashboard: back off polling to 15s when idle, stay at 5s when active —
  halves the steady-state DB query rate when nothing is processing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant