Skip to content

Improve performance of contentScopesCount via a request-scoped DataLoader - #6113

Merged
VPS-Obi merged 1 commit into
mainfrom
claude/content-scopes-count-dataloader
Aug 6, 2026
Merged

Improve performance of contentScopesCount via a request-scoped DataLoader#6113
VPS-Obi merged 1 commit into
mainfrom
claude/content-scopes-count-dataloader

Conversation

@fraxachun

@fraxachun fraxachun commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Problem

When the admin queries the users list (userPermissionsUsers), the contentScopesCount field resolver runs once per row. Each invocation calls getContentScopes(user), which recomputes the full list of available content scopes (build labels + uniqWith deduplication) from scratch. For a page of users this repeats the same shared computation N times, which becomes very slow when a project configures many content scopes.

Solution

Resolve the available content scopes once per request by routing contentScopesCount through a request-scoped DataLoader (UserContentScopesLoaderService), following the existing DataLoader pattern in cms-api (e.g. AttachedDocumentLoaderService). All users of a request are batched into a single getContentScopesForUsers(users) call that computes the available content scopes only once and then maps each user's scopes.

This PR keeps the existing lodash.uniqWith deduplication unchanged — the only change is how often the available content scopes are computed.

Speed comparison (25 users)

Measured by resolving contentScopesCount for 25 users (median of 3 runs), comparing the current per-row behavior against the DataLoader:

Typical case — each user resolves to a few content scopes

Available content scopes Current (per row) DataLoader Speedup
250 359 ms 15 ms ~25×
500 1 377 ms 55 ms ~25×
1 000 5 400 ms 209 ms ~26×
2 000 20 738 ms 821 ms ~25×

Worst case — every user resolves to all content scopes (the per-user uniqWith, which this PR does not change, then dominates in both variants):

Available content scopes Current (per row) DataLoader Speedup
250 558 ms 268 ms ~2.1×
500 2 229 ms 998 ms ~2.2×
1 000 9 305 ms 4 075 ms ~2.3×
2 000 35 057 ms 15 771 ms ~2.2×

So the DataLoader removes 24 of 25 redundant available-content-scope computations per request; the remaining cost is the per-user work, which is unchanged.

Example / test

user-permissions.service.spec.ts covers the batching guarantee (available content scopes computed only once for all users) and that each user's scopes are returned in input order.

Notes

Supersedes #5999 (which additionally replaced the uniqWith deduplication with a custom algorithm). This PR isolates just the DataLoader change.

https://claude.ai/code/session_01RGR2hkqpqcXL4xk7rGqz7n

When querying the users list, the contentScopesCount field resolver ran
once per row and recomputed the available content scopes each time. Route
it through a request-scoped DataLoader that batches all users of a request,
so the available content scopes are computed a single time per request.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RGR2hkqpqcXL4xk7rGqz7n
@nsams

nsams commented Aug 6, 2026

Copy link
Copy Markdown
Member

much better than #5999

@VPS-Obi
VPS-Obi merged commit 8c6be74 into main Aug 6, 2026
17 checks passed
@VPS-Obi
VPS-Obi deleted the claude/content-scopes-count-dataloader branch August 6, 2026 07:28
VPS-thodax added a commit that referenced this pull request Aug 13, 2026
…-scoped DataLoader (#6144)

Backport of #6113 to `v8.x.x`.

Note: the cherry-picked test file (`user-permissions.service.spec.ts`)
was converted from `vitest` to `jest`, since this branch still uses jest
for `@comet/cms-api`. No other changes were needed on top of the
cherry-pick.

**Verification on this branch:**
- `pnpm --filter '@comet/cms-api' run build` — passes
- `pnpm run lint` (in `packages/api/cms-api`) — passes
- `pnpm exec jest src/user-permissions/user-permissions.service.spec.ts`
— passes
- `demo/api` boots and initializes `AppModule`/`GraphQLModule` correctly
with the change (verified up to the expected Postgres connection error,
since this sandbox can't pull Docker images to run the full demo)

---
_Generated by [Claude
Code](https://claude.ai/code/session_012tKRdTMdhcfvHVrZdrBFz8)_

Co-authored-by: VPS - Franz Unger <franz.unger@vivid-planet.com>
Co-authored-by: Claude <noreply@anthropic.com>
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.

5 participants