Repository navigation
Hardcover language-evidence query times out (HTTP 408) on every author with Hardcover works: the slug/book_id _or defeats the index #3333
Description
Pre-flight
- I searched existing issues and this isn't a duplicate
- I'm running the latest version (check Settings → About)
Current behaviour
With OpenLibrary as primary and Hardcover as an enricher, the Hardcover language-evidence lookup fails on every author that has Hardcover supplement works. Author size makes no difference: an author with 4 works fails the same way as one with 99.
supplementing provider language evidence lookup failed; its works use the existing language fallbacks
provider=hardcover error="hardcover get author work language evidence: Post \"https://api.hardcover.app/v1/graphql\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)"
Each failure adds roughly 45–70 s to that author's refresh: three attempts (hardcoverMaxRetries = 2), each running into the 15 s client timeout, plus pacing between them. On a bulk refresh this is most of the time spent. While relinking ~250 Calibre-imported authors, a 4-work author took 50 s. With the Hardcover token cleared, the same refreshes take a few seconds each.
It isn't connectivity. Other Hardcover queries from the same host (author works, search) answer in under a second.
Cause
GetAuthorWorkLanguageEvidence in internal/metadata/hardcover/client.go (written in #2754) filters editions with:
{_or: [{book: {slug: {_in: $slugs}}}, {book_id: {_in: $bookIds}}]}This is an OR between a relationship predicate (book.slug) and a column predicate (book_id), and Hardcover apparently can't serve it from an index. The query runs until Hardcover cancels it at about 15 s and returns HTTP 408. Each branch on its own is fast and returns the same rows.
Measured by hand with the same token, languageCodes: ["eng", "en"] and limit set to the work count:
| Filter | Oliver Burkeman (24 works) | Joseph Campbell (99 works) |
|---|---|---|
_or as Bindery sends it (bookIds: []) |
408 after 15.5 s | 408 after 15.4 s |
Same, without distinct_on/order_by |
(not run) | 408 after 15.6 s |
book: {slug: {_in: $slugs}} only |
0.8 s, 9 rows | 0.7 s, 29 rows |
book_id: {_in: $bookIds} only (numeric ids) |
0.5 s, 9 rows | 0.7 s, 29 rows |
Since #3102 (for #3091) the aggregator also runs this lookup for Hardcover enricher works when OpenLibrary is primary, which is how OpenLibrary-primary setups started hitting it. #3102's follow-up query GetAuthorWorkKnownLanguages uses the same _or.
bookIds is filled only for numeric slugs. Usually it's [], and then the OR adds nothing except the slow query plan.
Expected behaviour
The lookup finishes in about a second and returns evidence, rather than timing out on every author and falling back.
Suggested fix
When bookIDs is empty, send the slug predicate alone. When both lists have entries, run one query per predicate and merge the results by key instead of combining them with _or.
Steps to reproduce
- Set OpenLibrary as primary, Hardcover as an enricher with a token, and
engas the metadata profile's allowed language. - Refresh any author that has Hardcover supplement works.
- The WARN above appears after about a minute.
To reproduce outside Bindery, send this to https://api.hardcover.app/v1/graphql with any valid token:
query GetAuthorWorkLanguageEvidence($slugs: [String!]!, $bookIds: [Int!]!, $languageCodes: [String!]!, $limit: Int!) {
editions(
where: {_and: [
{_or: [{book: {slug: {_in: $slugs}}}, {book_id: {_in: $bookIds}}]},
{language: {_or: [{code2: {_in: $languageCodes}}, {code3: {_in: $languageCodes}}]}}
]},
distinct_on: [book_id], order_by: [{book_id: asc}, {id: asc}], limit: $limit
) { book { id slug } language { code2 code3 language } }
}{
"slugs": ["four-thousand-weeks-time-and-how-to-use-it-2021", "meditations-for-mortals", "the-antidote", "help-how-to-be-slightly-happier-slightly-more-successful-and-get-a-bit-more-done", "why-are-we-so-angry", "the-power-of-negative-thinking-1a404c75-ff43-417b-95ac-3e6813000a40", "four-thousand-weeks-the-smash-hit-bestseller-that-will-change-your-life", "four-thousand-weeks-how-to-break-up-with-your-phone-hyperfocus-one-thing-4-books-collection-set", "get-happy-introduction-to-happiness", "de-4-weken-reset", "addicted-to-busy-why-life-has-got-so-hectic", "la-legge-del-contrario", "4000-tyzdnov", "aliveness", "2021-259f4abd-4969-4a78-a334-c734b22f41b6", "quattromila-settimane", "meditations-for-mortals-the-instant-sunday-times-bestseller-that-will-change-your-life", "oliver-burkemans-inconvenient-truth", "set-four-thousand-weeks-meditations-for-mortals", "das-gluck-ist-mit-den-realisten-warum-positives-denken-uberbewertet-ist", "cztery-tygodnie-dla-ciebie-sprawdz-jak-znalezc-czas-na-to-co-sie-liczy", "oliver-burkeman", "4000-thousand-weeks", "oliver-burkemans-2-book-set"],
"bookIds": [],
"languageCodes": ["eng", "en"],
"limit": 24
}That returns 408 after about 15 s. Replace the _or with {book: {slug: {_in: $slugs}}} and drop $bookIds, and you get 9 rows in under a second.
Trace logs
This excerpt is from the container log at INFO level; the WARN carries the full error. The timestamps show what each failing author costs. Authors without Hardcover works (M. Adithan, Nathalie Amiel) never run the query and finish in seconds.
10:57:08 INFO fetching books for author author="M. Adithan"
10:57:15 INFO author books synced author="M. Adithan" total=8
10:57:15 INFO fetching books for author author="Abi O. Aghayere"
10:57:18 INFO author books synced author="Abi O. Aghayere" total=3
10:57:18 INFO fetching books for author author="Roland Allen"
10:58:28 WARN supplementing provider language evidence lookup failed; its works use the existing language fallbacks provider="hardcover" error="hardcover get author work language evidence: Post \"https://api.hardcover.app/v1/graphql\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)"
10:58:28 INFO author books synced author="Roland Allen" total=36
10:58:28 INFO fetching books for author author="Nathalie Amiel"
10:58:29 INFO author books synced author="Nathalie Amiel" total=1
10:58:29 INFO fetching books for author author="Tarek Amr"
10:59:19 WARN supplementing provider language evidence lookup failed; its works use the existing language fallbacks provider="hardcover" error="hardcover get author work language evidence: Post \"https://api.hardcover.app/v1/graphql\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)"
10:59:20 INFO author books synced author="Tarek Amr" total=4
10:59:20 INFO fetching books for author author="Aranya"
11:00:07 WARN supplementing provider language evidence lookup failed; its works use the existing language fallbacks provider="hardcover" error="hardcover get author work language evidence: Post \"https://api.hardcover.app/v1/graphql\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)"
11:00:08 INFO author books synced author="Aranya" total=4
Deployment method
Docker (ghcr.io)
Bindery version
v1.43.0. main still sends the same query.
Environment details
Docker on a Linux VM. OpenLibrary is the primary provider, with Hardcover and DNB as enrichers, and the metadata profile language is eng. It showed up during the one-off relink of ~250 authors after a Calibre library import, but any author refresh hits it.
Activity
fix(hardcover): stop the language evidence query timing out (#3333) (#…