Speed up artist detail loading and "Watch All" operation #45
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Description:
Root cause analysis
Loading an artist's detail page and "Watch All" are extremely slow due to excessive sequential MusicBrainz API calls. The shared rate limiter (
shanty-data/src/http.rs) enforces 1.1s minimum between ALL MB requests across the app.Tracing the
enrich_artist()call chain (shanty-web/src/routes/artists.rs:256-594):For a typical artist with N release groups:
GET /ws/2/artist/{mbid}?fmt=jsonGET /ws/2/artist/{mbid}?inc=url-rels&fmt=jsonGET /ws/2/release-group?artist={mbid}&type=album|single|ep&fmt=json&limit=100GET /ws/2/release?release-group={rg_id}&fmt=json&limit=1first_release_mbidis None)GET /ws/2/release/{release_mbid}?inc=recordings&fmt=jsonTotal: 3 + up to 2N calls. For 20 release groups: up to 43 calls = ~47 seconds minimum.
The
add_artist()function (shanty-watch/src/library.rs:63-137) has a separate problem:provider.get_artist_releases(&mbid, 100)which hitsGET /ws/2/release/?artist={mbid}&fmt=json&limit=100get_release_tracks()callsenrich_artist()uses release groups (deduplicated) butadd_artist()uses releases (not deduplicated) — this is an architectural mismatchSpecific fixes
Deduplicate
get_artist_info()inenrich_artist(): It's called at line ~295 (artist resolution) and again at line ~335 (artist metadata fetch). The second call makes the exact same MB API request. Fix: if the artist was resolved by MBID in step 1, reuse that result for step 2.Fix
add_artist()to use release groups: Changeshanty-watch/src/library.rsto callprovider.get_artist_release_groups()instead ofprovider.get_artist_releases(). Then for each release group, resolve the release MBID and fetch tracks. This matches whatenrich_artist()does and eliminates redundant work on reissues/remasters.Eliminate
resolve_release_from_group()calls: Theget_artist_release_groups()MB response (shanty-data/src/musicbrainz.rs, theMbReleaseGroupResponsestruct) already includes areleasesarray with the first release's MBID. The code at line ~290 does extract this:first_release_mbid: rg.releases.and_then(|r| r.into_iter().next().map(|rel| rel.id)). But if the MB response doesn't include thereleasesfield (which it sometimes doesn't),first_release_mbidis None, triggering an extra API call viaresolve_release_from_group()for each such release group. Fix: request thereleasesinclude in the MB API call (add&inc=releasesor adjust the query), or accept the extra call but cache the result.Cache at the MB client level: Add an in-memory LRU cache in
MusicBrainzFetcherfor recent responses (keyed by URL). This prevents redundant calls within a single enrichment request — e.g., ifget_artist_info()and the resolution step both hit the same URL, the second one is a cache hit with zero delay.Show cached data immediately on the frontend: The two-phase loading in
shanty-web/frontend/src/pages/artist.rsalready loads a "quick" version first (?quick=true). Ensure the quick version returns cached tracklists (10-year TTL for watched artists) and only the slow version fetches uncached release groups. Consider loading uncached release groups one at a time and updating the UI progressively (WebSocket or polling).Key files
shanty-web/src/routes/artists.rs—enrich_artist()function (main bottleneck)shanty-watch/src/library.rs—add_artist()function (uses wrong MB endpoint)shanty-data/src/musicbrainz.rs—get_artist_release_groups(),resolve_release_from_group(),get_release_tracks()shanty-data/src/http.rs—RateLimiter(shared across all MB calls)Acceptance criteria:
get_artist_info()not called twice for same MBID in a single enrichmentadd_artist()uses release groups (not individual releases) — no redundant work on reissuesresolve_release_from_group()eliminated or minimized (first_release_mbid populated from MB response)