docs(mule-image): photos.hubris.network perf sweep changelog
Five fixes this session: backend out of dev mode, PIL fallback for iPhone Apple ProRAW Linear DNG, source root state classification so prune-missing handles renamed folders, smaller initial page size + idle polling backoff, and a partial index on photos that takes the list query from 25ms seq-scan to 0.1ms index-only scan. Also flags the deploy.sh --force-recreate gap that bit twice this session and the outstanding data-drift (4,154 orphan rows + one unregistered folder). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -80,12 +80,21 @@ For pushes from inside the LXC, gitea creds at `/etc/mule-deploy/git-credentials
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-05-10 — backend out of dev mode
|
||||
`docker-compose.yml` backend `command` was running `uvicorn … --reload` (single worker, file-watcher). Page loads fanned out ~15 parallel API calls that all serialized on one event loop. Switched to `--workers 2 --proxy-headers`. Verified: `docker inspect mulita-backend --format '{{.Config.Cmd}}'` shows the new flags; two uvicorn worker processes visible.
|
||||
### 2026-05-10 — `photos.hubris.network` perf sweep
|
||||
|
||||
Gotcha: the `docker compose up -d --build` in `deploy.sh` did not recreate the backend after this commit — only `--build` rebuilds images, and the `command` change is runtime-only. Had to run `docker compose up -d --no-deps --force-recreate backend` manually. Consider adding `--force-recreate` to `deploy.sh` if compose-only changes become common.
|
||||
User reported slow load. Five fixes shipped, in order:
|
||||
|
||||
Still on the table for further `photos.hubris.network` perf work: pre-generate `/data/proxies` web-sized webps (currently empty volume → first open of any RAW/HEIC is a sync decode), shrink the 500-photo default page size, and back off `worker-status` / `scan/status` polling when idle.
|
||||
1. **Backend out of dev mode.** `docker-compose.yml` `command:` was running `uvicorn … --reload` (single worker, file-watcher). Page loads fan out ~15 parallel API calls; they all serialized on one event loop. → `--workers 2 --proxy-headers`. Two uvicorn worker procs now.
|
||||
2. **iPhone Apple ProRAW / Linear DNG decode fixed.** rawpy 0.26.1 + LibRaw 0.22 rejects Apple Linear DNG (`Photometric Interpretation: Linear Raw`, 8-bit) as "Unsupported file format or not RAW file". Those files are TIFF containers with developed RGB inside and have no embedded preview to fall back to. Added PIL `Image.open(src_path)` as the last fallback in both `_generate_proxy_webp` (routers/photos.py) and `process_raw_image` (tasks/thumbs.py). ~1,300 iPhone DNGs were 415-ing on every detail view; now decode in <1s via PIL.
|
||||
3. **Reconcile DB with disk (renamed-folder case).** `prune_missing_photos` was skipping all 4,154 orphaned photo rows under SourceRoot `Taco and Muli - 2024 onward` (renamed in Nextcloud to `Photo Archive 2004-2024`) because the leaf path didn't resolve and the code assumed "drive unmounted → must skip". Added `_sr_state()` to classify as `present` / `renamed` (parent mount fine, leaf gone) / `unmounted` (parent inaccessible). Only `unmounted` still skips. Two stale source roots logged with a clearer hint pointing at `POST /api/v1/library/maintenance/prune-missing`. User has not yet been asked to run that — endpoint is ready when they are.
|
||||
4. **Frontend page size + idle polling.** `usePhotosQuery.ts` was first-fetching `per_page=500` (slow paint + 500 thumb requests at once). Split into `PER_PAGE_INITIAL=100` for first paint, `PER_PAGE_BACKGROUND=500` for the cursor-chain prefetch. Idle polling for `scan-status` and `worker-status` (`useScanActivity.ts`, `ScanProgress.tsx`) bumped from 10s/15s to 30s/30s while idle; active cadence (2s/3s) unchanged.
|
||||
5. **Partial index on `photos`.** Default list query `WHERE NOT is_trashed AND NOT is_hidden ORDER BY taken_at DESC NULLS LAST, id DESC LIMIT N` was doing a seq-scan + top-N heapsort (~25ms standalone, worse under concurrency). Added migration `0017_photos_list_index` creating `ix_photos_list_visible` (partial index on the sort key, restricted to visible rows). EXPLAIN now shows an Index Only Scan → **24.7ms → 0.097ms**, ~250× speedup.
|
||||
|
||||
**Deploy gotcha (repeated).** The `docker compose up -d --build` in `deploy.sh` does not recreate the backend when only runtime config (`command:` or env-only changes) or migration files change without changing the Dockerfile-bake outcome. Forced recreation with `docker compose up -d --no-deps --force-recreate backend` twice this session. **Action item:** add `--force-recreate` (or at least `--force-recreate backend` when backend files changed) to `/opt/mule-deploy/deploy.sh` so future deploys are reliable.
|
||||
|
||||
**Data drift still outstanding.** 4,154 photo rows + 1 unregistered folder (`Photo Archive 2004-2024`) on disk that's not a SourceRoot. The reconcile endpoints now work — user decides when to call them. The new folder needs to be added as a SourceRoot via the Settings UI before its files will be indexed.
|
||||
|
||||
**Proxy cache still empty** (`mule-image_proxies_data` volume is 4 KB). Pre-generating ~500–1,500 WebP proxies for non-web-safe formats would make first-open of every RAW/HEIC photo instant. Deferred — needs a one-shot script and the disk-space tradeoff isn't worth it until the data-drift reconcile happens first.
|
||||
|
||||
### 2026-04-28 — wiki entry created
|
||||
Initial documentation.
|
||||
|
||||
Reference in New Issue
Block a user