Repository navigation
Caching and request deduplication #2594
Description
Parent: #2592 — Reduce Hardcover API usage and support daily quotas
Pre-flight
- Searched existing issues; related reports are referenced by the parent.
- Checked the roadmap; this cache/deduplication scope is not already planned there.
Problem
SearchAuthorsWithOutcome and SearchBooks in internal/metadata/aggregator.go fan out to providers without server-side search caching. Metadata cache get/fetch/set sequences do not combine identical concurrent misses. GetEditions and GetEditionsFromProvider use different keys even when they resolve to the same Hardcover book.
The existing 24-hour cache is bounded but in memory, so restarts discard it. These redundant calls consume a plan-dependent daily allowance: see Hardcover Supporter.
Proposed solution
- Add bounded, short-lived provider-level search caching so ordinary searches and enrichment can reuse equivalent successful responses. Include query options and any relevant account/provider configuration in cache identity.
- Combine identical in-flight requests so concurrent misses perform one upstream fetch. Preserve cancellation semantics and avoid sharing mutable cached results unsafely.
- Resolve the provider before canonicalizing edition cache keys. Unify equivalent routed and explicit-provider lookups without confusing numeric IDs with slugs or mixing providers/accounts.
- Reuse existing metadata caching where practical. Evaluate persisted edition freshness for restart reuse; preserve an explicit refresh path and avoid treating partially populated local editions as a complete upstream snapshot.
- Cache legitimate empty results according to policy, but do not cache rate-limit/auth/network failures as successful empty data. Invalidate affected state on configuration changes.
Acceptance criteria
- Repeated equivalent searches and concurrent identical misses reduce counted upstream requests.
- Equivalent edition lookup routes share cached results; different providers, identities, and account scopes remain isolated.
- Cache bounds, expiry, cancellation, configuration changes, empty results, and failures have focused coverage.
- Existing search ranking, enrichment, edition completeness, and explicit refresh behavior are preserved.
Additional context
Covers review items 3 + 5. Coordinate with daily request accounting so cache hits are not charged locally. Related #2173 concerns skipping unchanged ABS items and is not replaced by this cache work.
Activity
vavallee commentedon Sep 26, 2026
Keeping this one, with #2592 now closed as a parent. The core claims all still verify: there is no search caching at either level, no singleflight anywhere in the tree, and
GetEditionsandGetEditionsFromProviderstill use different cache key namespaces.PR #2615 needs a rebase rather than a merge. It is around 70 commits behind, reports CONFLICTING, and it touches two files that moved underneath it:
aggregator_enrichment.go, which #2767 changed two days ago when field locks landed, andaggregator.go, where #2641 changedGetBookByISBNWithOutcome's signature.This is the semantic conflict shape that has broken main here before, where two individually green PRs on one file produce a broken merge. So the rebased branch wants a build and a full test run against merged main, not a green check on a stale base.
One item struck from the follow up list: #2611 fixed manual author refresh fetching past the 24 hour cache, which this PR had explicitly deferred to #2601. That is done.
Ordering note carried from the parent: #2593 lands first, because this builds on the quota carrying client.
perf(metadata): cache searches and deduplicate requests (vavallee#2594)
fix(metadata): keep enrichment cached without a hardcover token and r…
fix(metadata): copy the thin cluster sample when cloning a cached aut…
perf(metadata): cache searches and deduplicate requests (vavallee#2594)
fix(metadata): keep enrichment cached without a hardcover token and r…
fix(metadata): copy the thin cluster sample when cloning a cached aut…
fix(metadata): bound retries of a failing enricher and log only the f…
test(metadata): pin when a failed enrichment is short cached (vavalle…
perf(metadata): cache searches and deduplicate requests (vavallee#2594)
fix(metadata): keep enrichment cached without a hardcover token and r…
fix(metadata): copy the thin cluster sample when cloning a cached aut…
fix(metadata): bound retries of a failing enricher and log only the f…
test(metadata): pin when a failed enrichment is short cached (vavalle…
perf(metadata): cache provider searches and deduplicate requests (#2615)