- Add shared Arc<Semaphore> (1 permit) through main → AppState and jobs.
All heavy operations (catalog refresh, rebuild, nightly recompute,
factory reset) now serialise: a second trigger returns "already_running"
instead of spawning a parallel task that fights over the SQLite WAL lock.
- Scheduled nightly job acquires the semaphore too, so it waits rather
than stomping on a manual rebuild triggered at startup.
- Replace 90 × precompute_lightweight calls (90 separate transactions,
horizon fetched 90 times) with precompute_next_90_nights: one bulk
SELECT to find missing dates, horizon fetched once, all inserts in a
single transaction. Eliminates the 1–5s per-INSERT lock waits seen
when multiple jobs were competing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Factory reset endpoint clears computed tables (catalog, nightly_cache,
tonight, weather_cache), VACUUMs the DB, then rebuilds in background.
Preserves all user data (imaging_log, gallery, phd2_logs, horizon).
- Catalog rebuild now fetches data BEFORE touching the DB — network
failures no longer leave the catalog empty. DELETE + INSERT wrapped
in a single transaction via replace_catalog() so a mid-write failure
rolls back and old data is preserved.
- Added nightly_cache indexes and bumped pool to 10 connections with
30s acquire timeout to prevent exhaustion during rebuilds.
- Settings page: factory reset button with inline confirmation dialog.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The seasonal peak subquery used a correlated SELECT inside a GROUP BY,
causing a full nightly_cache scan per object (210-270s for 14k objects).
Replaced with a simple MAX() GROUP BY — now instant.
Also added three indexes on nightly_cache(night_date) that were missing
and causing all dashboard queries to run 2+ second full table scans.
Replaced 🔴 emoji in night mode buttons with CSS circles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>