The worker's MB browser (discography + artist search, used by monitor sweeps,
library scans, and Last.fm discovery) re-fetched every time. Route it through
the same ApiCache table + logical keys the web app uses, so a discography one
process fetched warms the other, and repeat lookups stop hitting MB.
- apicache.py cached_api(): read-through, serve-stale-on-outage, never caches
null, best-effort (a cache-DB error degrades to a live fetch, never breaks
scan/sweep/discovery). Its own DEDICATED autocommit connection so it can't
commit the worker's in-flight transaction, and autocommit avoids freezing
Postgres now() (which would break TTL math).
- CachingMbBrowser decorates MusicBrainzBrowser, serializing to the SAME
camelCase JSON shape the web writes (round-trip tested). registry.build_browser
wraps it when a DSN is present; the Last.fm source's browser rides it too.
- Hourly prune drops ApiCache rows > 90d so the table stays bounded.
- worker 213 tests / 7-skip.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>