Skip to content

fix(metadata): retry throttled lookups instead of recording them as no match - #883

Open
krejko wants to merge 1 commit into
Listenarrs:canaryfrom
nexalapp:up/rate-limits
Open

fix(metadata): retry throttled lookups instead of recording them as no match#883
krejko wants to merge 1 commit into
Listenarrs:canaryfrom
nexalapp:up/rate-limits

Conversation

@krejko

@krejko krejko commented Aug 23, 2026

Copy link
Copy Markdown

Bulk library import issues one metadata lookup per unmatched book — over a thousand on a large library — and nothing handled being throttled.

HandleTransientHttpError covers 5xx and 408 but not 429, the only status a rate limiter returns, so the Audible and Audnexus clients had exponential backoff that could never fire for the case it existed for. Adds a TooManyRequests clause and honours Retry-After when it exceeds the computed backoff, capped at 60s.

OpenLibraryService had no policy at all — registered via AddScoped with a bare HttpClient, which resolves the default unnamed client. Now a typed client sharing the policy.

The import store recorded failures as misses — the catch block set hasSearched: true with no match, making a 429 indistinguishable from "not on Audible" and excluding the row from re-runs. Failures are now tracked separately and stay retryable, with a count in the footer.

Also reworded the footer's "rate limit: 150/window", which matches no limiter in the codebase — ApiConfiguration.RateLimitPerMinute is never read by any production code.

Tests were each confirmed to fail with the corresponding fix reverted.

…o match

Bulk library import issues one metadata lookup per unmatched book - over
a thousand on a large library - but nothing in the pipeline handled being
throttled, so a rate-limited stretch silently produced books that looked
like they simply were not on Audible.

Polly's HandleTransientHttpError covers 5xx and 408 but deliberately not
429, which is the only status a rate limiter returns. The Audible and
Audnexus clients therefore had exponential backoff that could never fire
for the case it was needed. Adds an explicit TooManyRequests clause and
prefers the server's Retry-After hint over the computed backoff when it
is longer, capped at 60s so a mistaken header cannot stall the pipeline.

OpenLibraryService was registered with AddScoped and a bare HttpClient,
which resolves the default unnamed client and carries no policy at all,
so the fallback had no retry of any kind. It is now a typed client
sharing the same policy.

The import store's catch block marked a failed lookup hasSearched: true
with no match, making a throttled request indistinguishable from a
genuine miss and excluding the row from re-runs. Failures are tracked
separately, the row stays unprocessed so the next run retries it, and the
count is surfaced in the footer.

The footer also claimed a "rate limit: 150/window" that corresponds to no
limiter in the codebase; the ApiConfiguration.RateLimitPerMinute setting
it appears to reference is never read by any production code. Reworded to
what is actually true.

Each fix was confirmed to fail its test when reverted.
@krejko
krejko requested a review from a team August 23, 2026 03:20
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