Skip to content

Hardcover language-evidence query times out (HTTP 408) on every author with Hardcover works: the slug/book_id _or defeats the index #3333

Closed

Description

@BerzinsU

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

  1. Set OpenLibrary as primary, Hardcover as an enricher with a token, and eng as the metadata profile's allowed language.
  2. Refresh any author that has Hardcover supplement works.
  3. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:metadataMetadata providers and matchingbugSomething isn't workingpriority:mediumReal bug with a workaround

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions