Skip to content

Retry MusicBrainz work search instead of reporting an outage as no re… - #223

Merged
dprodger merged 1 commit into
mainfrom
fix/musicbrainz-search-503-retry
Sep 13, 2026
Merged

dprodger merged 1 commit into
mainfrom
fix/musicbrainz-search-503-retry

Conversation

@dprodger

Copy link
Copy Markdown
Owner

…sults

MusicBrainz sheds load with a 503 ("The MusicBrainz web server is currently busy") a few percent of the time. search_works_multi was the one fetch in the client with no retry: it called raise_for_status(), the HTTPError fell into the broad RequestException handler, and the caller got an empty list. The route then returned a healthy 200 with zero results, so the app told users the song did not exist in MusicBrainz whenever the service hiccuped.

Backend:

  • Add MusicBrainzUnavailable, raised only after the retry budget is spent.
  • Give the work search three attempts with 1s/2s backoff, shorter than the importers' 2s/4s because a user is waiting on a search sheet. Retry 503, 429, timeouts and connection errors; do not retry other 4xx, which are our own bad query.
  • Return 503 from /musicbrainz/works/search when MusicBrainz is unreachable, keeping 200-with-empty-results to mean "answered, no matches".
  • Fix the apostrophe normalization next to it. It read replace("'", "'") with ASCII on both sides, an editor autocorrect having flattened the curly one, so it did nothing. Now maps the five variants using \u escapes, as normalize_title already does.

Apps:

  • searchMusicBrainzWorks returns MusicBrainzSearchResult rather than collapsing every failure into an empty array.
  • Both search sheets gain a "Search Unavailable" state with Try Again, distinct from the empty state that links out to musicbrainz.org.

Old app builds are unaffected: they already treated any non-200 as an empty result, and they pick up the server-side retry without changing.

Tests: eight cases covering persistent 503, transient 503, timeout, genuine empty result, un-retried 400, the apostrophe substitution, and both route status codes.

…sults

MusicBrainz sheds load with a 503 ("The MusicBrainz web server is currently
busy") a few percent of the time. search_works_multi was the one fetch in
the client with no retry: it called raise_for_status(), the HTTPError fell
into the broad RequestException handler, and the caller got an empty list.
The route then returned a healthy 200 with zero results, so the app told
users the song did not exist in MusicBrainz whenever the service hiccuped.

Backend:
- Add MusicBrainzUnavailable, raised only after the retry budget is spent.
- Give the work search three attempts with 1s/2s backoff, shorter than the
  importers' 2s/4s because a user is waiting on a search sheet. Retry 503,
  429, timeouts and connection errors; do not retry other 4xx, which are
  our own bad query.
- Return 503 from /musicbrainz/works/search when MusicBrainz is unreachable,
  keeping 200-with-empty-results to mean "answered, no matches".
- Fix the apostrophe normalization next to it. It read replace("'", "'")
  with ASCII on both sides, an editor autocorrect having flattened the curly
  one, so it did nothing. Now maps the five variants using \u escapes, as
  normalize_title already does.

Apps:
- searchMusicBrainzWorks returns MusicBrainzSearchResult rather than
  collapsing every failure into an empty array.
- Both search sheets gain a "Search Unavailable" state with Try Again,
  distinct from the empty state that links out to musicbrainz.org.

Old app builds are unaffected: they already treated any non-200 as an empty
result, and they pick up the server-side retry without changing.

Tests: eight cases covering persistent 503, transient 503, timeout, genuine
empty result, un-retried 400, the apostrophe substitution, and both route
status codes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dprodger
dprodger merged commit d433536 into main Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant