You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Catalog search latency under the new 500ms LML enrichment budget cap (#618) is bounded but not optimal. Every search returns rows where library.artwork_url IS NULL, the enrichWithArtwork service makes parallel LML lookups for those rows, and either the lookups complete within 500ms or the budget fires and we return uncached rows. Either way, every search pays at minimum a slice of the budget on first load for un-cached albums.
Current state on prod:
SELECT count(*) FILTER (WHERE artwork_url IS NOT NULL) AS with_art,
count(*) FILTER (WHERE artwork_url IS NULL) AS without,
count(*) AS total
FROM wxyc_schema.library;
→ with_art=155, without=64008, total=64163 — 0.24% of library rows have artwork cached.
Tonight's entity_resolution run lifted Discogs identity coverage on wxyc_schema.artists to 24.3% (5,785 / 23,816 artists). Of those, the corresponding library rows are the search-time enrichment cache hits we'd actually expect to succeed if we proactively backfill artwork_url for them.
Cost model (2026-04-29 update)
After verifying the LML code path: artwork URLs are sourced from discogs-cache.release.artwork_url, populated by discogs-etl/scripts/import_csv.py:import_artwork from release_image.csv in the monthly Discogs XML dump. For any release in the cache, LML serves artwork as a local PG read — no Discogs API hop.
This means the original "30 minutes at 10 req/s" estimate is too pessimistic. The actual cost shape per row:
BS → LML round-trip (~5–20ms each).
LML → discogs-cache local PG read (sub-millisecond when cached).
BS write back to RDS.
Live Discogs API hits only happen for the residual where release_image.csv had no entry at all, bounded by Discogs's image coverage. LML rate limits are no longer the bottleneck.
Framing
This is a one-shot warm of the cache, not a recurring sync. Once library.artwork_url is populated, search-time enrichWithArtwork short-circuits and the 500ms enrichment budget stops firing for those rows. Staleness risk on the i.discogs.com URL is bounded by the monthly cache rebuild cadence (WXYC/discogs-etl#125) and Discogs's CDN URL stability.
For the 75% of rows whose artist isn't in Discogs, search-time enrichment is hopeless regardless and the budget will keep firing — that's the long-term concern addressed by other strategies (LML #207 wires up MusicBrainz/Wikidata/Spotify identity sources; LML #211 closes the matching gap). This issue addresses the resolvable slice.
Proposal
Mirror the pattern of jobs/library-artist-name-backfill/: a one-shot batched UPDATE job under jobs/library-artwork-url-backfill/ that:
Queries every library row joined to artists where a.discogs_artist_id IS NOT NULL AND l.artwork_url IS NULL. Expected count after tonight's reconcile: ~18,500.
For each batch of N rows, calls POST /api/v1/lookup against LML (or lookupMetadata directly if running inside the BS process) with the artist + album.
Writes the resulting artwork_url back. If the lookup fails or returns no artwork, leave NULL — the search-path enrichment will retry and fail cleanly.
Idempotent via WHERE artwork_url IS NULL AND a.discogs_artist_id IS NOT NULL filter — re-running picks up only rows the previous run didn't finish.
Run procedure mirrors tonight's artist-name backfill: Manual Build & Deploy with target=library-artwork-url-backfill, then docker run --rm --env-file .env <image> on EC2.
Expected impact
Once complete:
~18,500 rows have artwork_url set, so enrichWithArtwork short-circuits on them.
For searches whose top-N results all have cached artwork, the enrichment budget never fires — search latency drops to baseline RTT (~370ms) consistently.
The 500ms budget continues to protect the path for searches that hit Discogs-unresolvable artists.
Caveats
Should not run against rows where artwork_url is non-null but stale; this job only touches NULL rows. Stale-artwork refresh is a separate, cron-scheduled concern.
Throughput can be tuned higher than the original 10 req/s estimate now that LML rate limits aren't the bottleneck — local round-trip is.
Acceptance
New job jobs/library-artwork-url-backfill/ mirroring library-artist-name-backfill/ (per-batch transactions, NULL-filter for idempotency, verification step at end).
Documented run procedure in CLAUDE.md.
Sentry / JSON logging via the standard Phase A observability contract (tool=library-artwork-url-backfill, step=batch-N, run_id UUID per run).
Post-run verification: count of artwork_url IS NOT NULL jumped to expected 18,500-ish.
Problem
Catalog search latency under the new 500ms LML enrichment budget cap (#618) is bounded but not optimal. Every search returns rows where
library.artwork_url IS NULL, theenrichWithArtworkservice makes parallel LML lookups for those rows, and either the lookups complete within 500ms or the budget fires and we return uncached rows. Either way, every search pays at minimum a slice of the budget on first load for un-cached albums.Current state on prod:
→
with_art=155, without=64008, total=64163— 0.24% of library rows have artwork cached.Tonight's entity_resolution run lifted Discogs identity coverage on
wxyc_schema.artiststo 24.3% (5,785 / 23,816 artists). Of those, the correspondinglibraryrows are the search-time enrichment cache hits we'd actually expect to succeed if we proactively backfillartwork_urlfor them.Cost model (2026-04-29 update)
After verifying the LML code path: artwork URLs are sourced from
discogs-cache.release.artwork_url, populated bydiscogs-etl/scripts/import_csv.py:import_artworkfromrelease_image.csvin the monthly Discogs XML dump. For any release in the cache, LML serves artwork as a local PG read — no Discogs API hop.This means the original "30 minutes at 10 req/s" estimate is too pessimistic. The actual cost shape per row:
Live Discogs API hits only happen for the residual where
release_image.csvhad no entry at all, bounded by Discogs's image coverage. LML rate limits are no longer the bottleneck.Framing
This is a one-shot warm of the cache, not a recurring sync. Once
library.artwork_urlis populated, search-timeenrichWithArtworkshort-circuits and the 500ms enrichment budget stops firing for those rows. Staleness risk on thei.discogs.comURL is bounded by the monthly cache rebuild cadence (WXYC/discogs-etl#125) and Discogs's CDN URL stability.For the 75% of rows whose artist isn't in Discogs, search-time enrichment is hopeless regardless and the budget will keep firing — that's the long-term concern addressed by other strategies (LML #207 wires up MusicBrainz/Wikidata/Spotify identity sources; LML #211 closes the matching gap). This issue addresses the resolvable slice.
Proposal
Mirror the pattern of
jobs/library-artist-name-backfill/: a one-shot batched UPDATE job underjobs/library-artwork-url-backfill/that:libraryrow joined toartistswherea.discogs_artist_id IS NOT NULL AND l.artwork_url IS NULL. Expected count after tonight's reconcile: ~18,500.POST /api/v1/lookupagainst LML (orlookupMetadatadirectly if running inside the BS process) with the artist + album.artwork_urlback. If the lookup fails or returns no artwork, leave NULL — the search-path enrichment will retry and fail cleanly.WHERE artwork_url IS NULL AND a.discogs_artist_id IS NOT NULLfilter — re-running picks up only rows the previous run didn't finish.Run procedure mirrors tonight's artist-name backfill:
Manual Build & Deploywithtarget=library-artwork-url-backfill, thendocker run --rm --env-file .env <image>on EC2.Expected impact
Once complete:
artwork_urlset, soenrichWithArtworkshort-circuits on them.Caveats
artwork_urlis non-null but stale; this job only touches NULL rows. Stale-artwork refresh is a separate, cron-scheduled concern.Acceptance
jobs/library-artwork-url-backfill/mirroringlibrary-artist-name-backfill/(per-batch transactions, NULL-filter for idempotency, verification step at end).tool=library-artwork-url-backfill,step=batch-N,run_idUUID per run).artwork_url IS NOT NULLjumped to expected 18,500-ish.Blocked by
artistsis the result of the LML entity-resolution pipeline tracked there. Without Run LML entity-resolution pipeline to populate entity.identity in prod #572's runs in production, this backfill addresses a smaller resolvable slice.Cross-repo risks
/lookupdoesn't enrich direct library hits with Discogs artwork. Direct-hit branch may return nullartworkeven when a Discogs match exists; affects observed hit rate of this backfill.Out of scope
References
enrichWithArtworkbudget capped in Cap LML artwork enrichment on the catalog search hot path #618.jobs/library-artist-name-backfill/.discogs-etl/scripts/import_csv.py:443(artwork import) +library-metadata-lookup/discogs/cache_service.py:340,487(local PG read).