Skip to content

perf(books): bulk-load editions before format-sync work grouping (N+1 per save) - #265

Open
jordanfelle wants to merge 2 commits into
Chaptarr:developfrom
jordanfelle:perf-workgroups-bulk-load-editions
Open

jordanfelle wants to merge 2 commits into
Chaptarr:developfrom
jordanfelle:perf-workgroups-bulk-load-editions

Conversation

@jordanfelle

Copy link
Copy Markdown

Problem

Book.Editions is lazy-loaded. In the format-sync pass, BuildWorkGroups (BookIdentity.GetProviderIdentityTokens -> BookEditionIdentity.GetOrderedEditions) and CloneStoredBook (GetAsin) both read it, and the pass is handed every one of an author's books on each save (UpdateMany, insert defaults, bulk reconcile, PersistWithLifecycle, SetMonitored*). Each book costs its own Editions query, on every save.

Found by sampling a live bulk refresh with dotnet-stack (4 samples, one busy thread): two landed in GetOrderedEditions -> LazyLoad() under BuildWorkGroups, the other two in Npgsql waiting on an UPDATE / DELETE from the same loop. Postgres CPU and memory were not saturated; the thread was serialised on round trips.

Change

  • PreloadEditions(authorId, books): one GetEditionsByAuthor per author, called right after the author's books are fetched and before they are cloned or grouped.
  • PreloadEditionsByBook(books): one IN query for a specific set (the stored copies cloned in UpdateMany, PersistWithLifecycle, SetMonitored, SetMonitoredForMediaType).
  • Only books whose editions have not loaded are filled, with the same rows the lazy loader returns (Editions by BookId), so already-loaded books keep their in-memory editions and behaviour is otherwise unchanged.

Tests

  • update_many_should_load_author_editions_in_one_query_instead_of_one_per_book: 25 books, no per-book lazy loads, at most two bulk author queries.
  • update_many_should_give_each_book_only_its_own_editions_and_not_touch_loaded_books.
  • Both fail without the change (verified). Full Chaptarr.Core.Test: 3037 passed. The strict edition-service proxy in BookProviderIdCanonicalizationFixture gains the bulk GetEditionsByBook overload.

Not measured

I have no before/after wall-clock for a refresh yet; the claim is the query-count change, not a speedup figure.

…nstead of one query per book

Book.Editions is lazy-loaded. BuildWorkGroups (BookIdentity.GetProviderIdentityTokens ->
BookEditionIdentity.GetOrderedEditions) and CloneStoredBook (GetAsin) both read it, and the
format-sync pass hands them every one of an author's books on each save (UpdateMany,
insert defaults, bulk reconcile). That issued one Editions query per book, per save - a
stack sample of a bulk refresh spent most samples in that lazy load or in the DB round
trips around it, on a single thread.

Add PreloadEditions (one GetEditionsByAuthor per author) and PreloadEditionsByBook (one
IN query for a given set) and call them right after the books are fetched and before they
are cloned or grouped. Only books whose editions have not loaded are filled, with the same
rows the lazy loader returns (Editions by BookId), so already-loaded books keep their
in-memory editions.

Tests: an UpdateMany over 25 books performs no per-book lazy loads and one bulk author
query; each book gets only its own editions and a loaded book is left untouched. Both fail
without the change. The strict edition-service proxy in BookProviderIdCanonicalizationFixture
learns the bulk GetEditionsByBook overload.
…re-running the author-wide editions query

Adversarial review of Chaptarr#265: the second PreloadEditions call in GetSyncUpdatesForMutations re-loaded every edition for the author whenever the caller's changed books were unloaded (the normal case). Use the by-book IN query for just those books, and pin exactly one author-wide query in the test.
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