Skip to content

perf(core): batch stockLevels lookups per request - #15

Closed
brmk wants to merge 1 commit into
masterfrom
perf/stock-levels-resolver-batch
Closed

brmk wants to merge 1 commit into
masterfrom
perf/stock-levels-resolver-batch

Conversation

@brmk

@brmk brmk commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

Description

The Admin API ProductVariant.stockLevels field resolves one channel-filtered query per variant, so every variant list page in the dashboard costs one stock query per row. vendurehq#5224 batched the sibling path behind getAvailableStock (stockOnHand / stockAllocated) but did not touch stockLevels.

getStockLevelsForVariant now reads through a per-request DataLoader held in RequestContextCacheService, the same mechanism as vendurehq#5224. The batch runs the same joins and channel filter with productVariantId IN (...) and groups the rows by variant. The loader lives on the ctx that supplies the channel filter, so it cannot mix channels. cache: false batches without memoizing, so a write earlier in the request is not masked. Public signature unchanged.

The batch orders by stockLocationId. The single-variant query returned that order implicitly through the unique (productVariantId, stockLocationId) index, and the multi-location e2e suite asserts on it; an IN (...) query does not guarantee it.

Measured with a counting TypeORM logger on the core e2e fixture, productVariants(options: { take: 40 }) with stockLevels selected: 34 stock queries → 1, on sqljs, Postgres 16, MySQL 8 and MariaDB 11.5. The stock-control, stock-control-multi-location and stock-location e2e suites pass unchanged on all four. Seven unit cases added to stock-level.service.spec.ts.

Relates to vendurehq#5224

Breaking changes

None. No schema, GraphQL or config change.

Checklist

📌 Always:

  • I have set a clear title
  • My PR is small and contains a single feature
  • I have checked my own PR

👍 Most of the time:

  • I have added or updated test cases
  • I have updated the README if needed

@brmk
brmk force-pushed the perf/stock-levels-resolver-batch branch from b6b2923 to c7fe51e Compare September 10, 2026 14:43
The Admin API `ProductVariant.stockLevels` field ran one channel-filtered
query per variant, so a list page of 34 variants cost 34 stock queries.
vendurehq#5224 batched the sibling `getAvailableStock` path but left this one alone.

`getStockLevelsForVariant` now reads through a per-request DataLoader with
`cache: false`, as in vendurehq#5224: same joins and channel filter, `IN (...)` in
place of `= :id`, rows grouped by variant. The batch orders by
`stockLocationId`, which the per-variant query returned implicitly and the
multi-location e2e suite relies on.

Same 34-variant page: 34 stock queries → 1.

Relates to vendurehq#5224
@brmk
brmk force-pushed the perf/stock-levels-resolver-batch branch from c7fe51e to 8129c3a Compare September 10, 2026 19:39
@brmk brmk closed this Sep 10, 2026
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.

2 participants