perf(charges-matcher): stream queue match suggestions with @defer (3/3)#3984
perf(charges-matcher): stream queue match suggestions with @defer (3/3)#3984gilgardosh wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements lazy loading and streaming of charge-match suggestions using GraphQL @defer and a server-side DataLoader. This allows the client to render base charges immediately while suggestions load in the background. The review feedback points out a critical issue where a deferred payload error could unmount the entire UI, and suggests adjusting the error handling condition to keep the UI visible when data is present.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| )} | ||
|
|
||
| {fetching ? ( | ||
| {fetching && !data ? ( |
There was a problem hiding this comment.
With the introduction of @defer for streaming suggestions, a GraphQL error in any of the deferred payloads will populate the error object. Currently, if error is truthy, the entire main UI is unmounted and replaced with null (line 165: ) : error ? null : (). This means a single deferred scoring failure will cause the already-rendered base charges to disappear, defeating the purpose of lazy loading.
To prevent this, we should still render the main UI if data is present, even if there is an error (which will still be displayed in the Alert at the top). Consider changing line 165 to check !data instead of error:
) : !data ? null : (dd28ef4 to
203d4ce
Compare
d00be5a to
606c4ec
Compare
203d4ce to
be0ff13
Compare
606c4ec to
3fffc51
Compare
be0ff13 to
72f4140
Compare
3fffc51 to
666d64f
Compare
Make `ChargeWithSuggestions.suggestions` a lazy field resolver so the awaiting- match queue can return base charges without scoring them up front. A new per-operation DataLoader coalesces all suggestion loads into a single `findMatchesForCharges` call, so the shared candidate pool is still built once. - Server: add a `ChargeWithSuggestions` codegen mapper (suggestions optional), a `suggestions` field resolver, and the batching loader. `BY_DATE` returns the page unscored; `BY_SCORE` still evaluates up front (needed to sort) and carries the suggestions on each item. - Client: wrap `suggestions` in a deferred inline fragment so the queue list paints immediately, gating the initial skeleton on `fetching && !data` and showing a per-card "Scoring suggestions…" state until the deferred payload arrives. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVAbrnSqJcoqq8BRyDStKY
666d64f to
c98f6d9
Compare
🚀 Snapshot Release (
|
| Package | Version | Info |
|---|---|---|
@accounter/client |
0.1.0-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 |
npm ↗︎ unpkg ↗︎ |
@accounter/gmail-listener |
0.1.3-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 |
npm ↗︎ unpkg ↗︎ |
@accounter/israeli-vat-scraper |
0.1.13-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 |
npm ↗︎ unpkg ↗︎ |
@accounter/modern-poalim-scraper |
0.10.7-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 |
npm ↗︎ unpkg ↗︎ |
@accounter/scraper-app |
0.0.3-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 |
npm ↗︎ unpkg ↗︎ |
@accounter/server |
0.2.0-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 |
npm ↗︎ unpkg ↗︎ |
@accounter/shaam-uniform-format-generator |
0.2.7-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 |
npm ↗︎ unpkg ↗︎ |
@accounter/shaam6111-generator |
0.1.9-alpha-20260720115807-16e3e4b6d6d1e5fc9ba80097298a34fb9fd69bb1 |
npm ↗︎ unpkg ↗︎ |
Context
PR 3 of 3 (final) in the
chargesAwaitingMatchQueueperformance series. Stacked on #3983 (base is that PR's branch; retarget down the stack tomainas the earlier PRs merge).@deferthe match suggestions so base charges paint immediatelyProblem
Even with PRs 1–2 the queue was still computed eagerly: the resolver scored every charge on the page before returning anything, so the client couldn't render until all scoring finished.
Change
Make match suggestions lazy and stream them via
@defer.Server
ChargeWithSuggestions.suggestionsis now a field resolver, backed by a per-operation DataLoader (suggestionsByChargeIdLoader) that coalesces every requested charge into onefindMatchesForChargescall — so the shared candidate pool from perf(charges-matcher): build match candidate pool once per queue request (1/3) #3982 is still built once.ChargeWithSuggestions → ChargeWithSuggestionsMappermakessuggestionsoptional on the parent.BY_DATEreturns the page unscored; suggestions resolve lazily.BY_SCOREstill evaluates the window up front (scores are required to sort) and carries the suggestions on each item, so the field resolver returns them directly.Client
ChargesAwaitingMatchQueuequery wrapssuggestionsin a... on ChargeWithSuggestions @deferinline fragment (mirroring the existingChargeMetadata @deferpattern).fetching && !data(the repo's deferred-query convention), and the Suggested-Match pane shows a per-card "Scoring suggestions…" state until each charge's deferred payload arrives.Files
codegen.ts,charges-matcher/types.ts— new mapper.providers/queue-match-evaluator.provider.ts— batching suggestions loader.resolvers/index.ts—suggestionsfield resolver.resolvers/charges-awaiting-match-queue.resolver.ts—BY_DATEreturns unscored page.client/.../queue-query.graphql.ts,client/.../index.tsx—@defer+ loading state.BY_DATEbehavior + new loader test.Testing
yarn generate(mapper → resolver + client types), server unit/integration tests, and a manual pass on the matching screen confirming base charges render immediately with suggestions streaming in.🤖 Generated with Claude Code
Generated by Claude Code