fix(metadata): restore author/series catalog cache on SQLite#649
Open
kevinheneveld wants to merge 1 commit into
Open
fix(metadata): restore author/series catalog cache on SQLite#649kevinheneveld wants to merge 1 commit into
kevinheneveld wants to merge 1 commit into
Conversation
The four catalog-cache read paths in AudiobookRepository ordered results by LastFetchedAt.GetValueOrDefault(UpdatedAt), which the SQLite EF Core provider cannot translate. The query threw at execution time and each caller's best-effort try/catch swallowed it as a cache miss, so the persisted author/series catalog cache never returned a hit — every author and series page re-fetched the full catalog from Audible on each load. Use null-coalescing (entry.LastFetchedAt ?? entry.UpdatedAt), which maps to SQL COALESCE with identical freshest-first ordering but is translatable. Add regression coverage that exercises these reads against the real SQLite provider; the EF InMemory provider tolerates the untranslatable LINQ and so could not catch this. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1832d8f to
a88bbf0
Compare
therobbiedavis
approved these changes
Jun 9, 2026
Collaborator
There was a problem hiding this comment.
@kevinheneveld This looks good to me. Please delete the changelog and update the PR description to match the template.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The persisted author and series metadata cache never returns a hit on SQLite, so every author and series page re-fetches the full catalog from Audible on each load. The cache read orders by a
Nullable<DateTime>.GetValueOrDefault(...)expression the SQLite EF Core provider can't translate; the query throws, the caller's best-effortcatchswallows it as a cache miss, and the cache is effectively dead. This switches the ordering to a translatableCOALESCEand adds regression coverage that runs against the real SQLite provider.Why this matters
Open any author or series page twice. The catalog should come from the persisted cache the second time, but instead each visit hits Audible again — slower pages, and avoidable load on the upstream Audible endpoints (and more exposure to their rate limits). On a library with many authors/series, ordinary browsing keeps re-fetching catalogs that were already cached minutes ago.
The cache looks like it works in tests because the EF InMemory provider happily evaluates the same LINQ that SQLite rejects — so the failure only appears against a real database.
Implementation notes
listenarr.infrastructure/Persistence/Repositories/AudiobookRepository.cs— four cache-read methods (GetCachedAuthorByNameAsync,GetCachedAuthorByAsinAsync,GetCachedSeriesByNameAsync,GetCachedSeriesByAsinAsync).OrderByDescending(e => e.LastFetchedAt.GetValueOrDefault(e.UpdatedAt))→OrderByDescending(e => e.LastFetchedAt ?? e.UpdatedAt). The null-coalescing form maps to SQLCOALESCE(LastFetchedAt, UpdatedAt); identical "freshest-first" ordering semantics, but translatable. An inline comment at each site records why, so the pattern doesn't regress.Microsoft.Data.Sqlite:memory:) rather than the EF InMemory provider, because InMemory can't reproduce the translation failure. The new tests fail against the current code withTranslation of method 'System.DateTime?.GetValueOrDefault' failedand pass with the fix.What this PR does NOT include
Test plan
dotnet test tests/Listenarr.Tests.csproj --filter FullyQualifiedName~AudiobookRepository_CatalogCacheReadTests— 3/3 passing (new).canarywith theGetValueOrDefaulttranslation error — they guard the actual regression.dotnet test tests/Listenarr.Tests.csproj— full suite 685/685 passing.