You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
perf(client): reuse unchanged pr links while streaming - #37039
Streaming in a thread with many linked pull requests repeats work over every link on each shell tick. While a run streams, the server resends the thread's whole shell several times a second, and every linked pull request arrives as a freshly decoded object. Each tick then:
deep-compares every link through the Schema equivalence (sameThreadShell, sameApartFromStreaming),
re-resolves the current pull request chains from scratch in each component that reads them (SidebarThreadRow, ThreadStatusIndicators, threadReference, pullRequestDetail.logic),
rebuilds savedPullRequestSummariesAtom from every link in the environment. Each link's URL is parsed in threadPullRequestLinkToSummary, and its key is normalized again because savedPullRequestKey wraps it in a fresh object, so the WeakMap key cache misses. This atom rebuilds on any thread's tick, not just the streaming one.
The thread this came from has 322 linked PRs; the environment has 1,340 links across 312 threads.
Change
shellReducer: a thread.updated delta, or a snapshot row, whose links equal the prior row's keeps the prior links array. Later equivalence checks then stop at identity, and per-link caches stay warm.
resolveThreadCurrentPullRequest: cached per links array in a WeakMap (arrays are immutable).
savedPullRequestSummariesAtom: keys by the link itself, so the existing key cache hits, and caches each link's summary per link object.
Behavior is unchanged: a changed link yields a new array, so every cache recomputes.
Scope
A real performance problem for long-running threads that link many PRs (stacks, sweeps). client-runtime and source-control-core are shared with mobile, so the reducer and current-PR changes apply there too. savedPullRequestSummariesAtom is web-only.
Verification
Real data, not fixtures: a copy of the actual 322-PR thread (22,900 items, 486-thread family) and its environment, made with migrate-dev-db. The server and browser ran inside a sandbox (bubblewrap with no network, read-only filesystem, provider credentials hidden), so nothing could reach the real worktree or a provider. The app was a production build of current main with sourcemaps. In Playwright I switched the thread's composer to the Replay (dev) Showcase model, streamed one turn, and captured a CDP trace with the V8 CPU profiler. Base and branch builds ran alternately, 3 runs each, with CPU profiles symbolicated through the build's sourcemaps:
one streamed turn
base
this PR
main-thread busy
1,262 / 1,193 / 1,236 ms
1,103 / 865 / 1,097 ms
Schema equivalence (toEquivalence)
84 / 78 / 79 ms
43 / 45 / 43 ms
Schema decode (SchemaParser)
93 / 89 / 91 ms
60 / 61 / 60 ms
resolveThreadCurrentPullRequest
60 / 59 / 62 ms
24 / 25 / 24 ms
savedPullRequestSummariesAtom
36 / 38 / 38 ms
3 / 3 / 3 ms
A reducer-only build reached about 1,150 ms busy and left the summaries rebuild at about 37 ms and current-PR resolution at about 35 ms. So all three changes carry weight.
The longest task is the same in every run (about 250 ms). It is the profiler starting and has no samples.
shellReducer.test.ts gets one case: a changed row with equal links keeps the prior array on both the single and batched paths, and a changed link replaces it. It fails on main and passes here.
vp run check:changed passes (19 packages typechecked).
Not measured: several agents streaming at once, which multiplies the environment-wide summaries rebuild; the mobile client.
Macroscope's review found this PR approvable — This change reuses unchanged PR links and caches derived PR summaries during streaming. Its effects are confined to client-side computation and presentation, with no product-default or persistent-data changes.
You can add or adjust custom eligibility rules. Learn more.
Reviewing files that changed from the base of the PR and between b90e6ad and 77cb885.
You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.
Use the checkbox below for a quick retry:
Trigger review
No actionable comments were generated in the recent review.
Walkthrough
Thread updates now retain equivalent pull-request link arrays. Pull-request resolution and saved-link summary conversion cache derived results.
Changes: Pull-request link handling
Layer / File(s)
Summary
Retain equivalent link arrays packages/client-runtime/src/state/shellReducer.ts, packages/client-runtime/src/state/shellReducer.test.ts
Snapshot and streaming updates reuse existing pull-request arrays when their contents are equivalent. Tests cover single and batched updates, including changed links.
Cache current pull-request resolution packages/source-control-core/src/shared/threadPullRequests.ts
Resolution results, including null, are cached by links-array identity.
The change reuses unchanged pull-request link arrays and caches derived results to cut repeated work while streaming. No concrete regression was found, and the reducer tests cover both equal and changed links. The failing CI test is an unrelated port conflict.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title "perf(client): reuse unchanged pr links while streaming" matches the main change. The PR keeps the prior links array when streamed thread rows have equal links, and it adds caches for curren…
Description check
The description follows the repository template. It has Problem, Change, Scope, and Verification sections, and it uses a conventional-style summary. The Problem section explains the repeated work duri…
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Title check
Explanation
The title "perf(client): reuse unchanged pr links while streaming" matches the main change. The PR keeps the prior links array when streamed thread rows have equal links, and it adds caches for current pull request resolution and saved summaries. The title is short, uses a conventional commit prefix, and names the primary goal. It does not mention the caching of current-PR resolution or summaries, but those are secondary aspects.
Full details: Description check
Explanation
The description follows the repository template. It has Problem, Change, Scope, and Verification sections, and it uses a conventional-style summary. The Problem section explains the repeated work during streaming, with the affected components and atoms. The Change section lists the three modifications. The Scope section names the shared mobile impact and the web-only atom, and it links the related PRs #37001 and #37002 as context. It does not link a bug issue or a maintainer request, which the template asks for. The Verification section gives concrete measurements, a regression test, and a check run. It also states what was not measured. The description includes a 'clank context' details wrapper that is not in the template. The description has no agent/model line at the end, although the template asks for one if an agent was used. These gaps are minor.
Finishing Touches
Generate unit tests (beta)
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
A streaming thread resends its whole shell on each tick, with every linked pull request decoded into new objects. A thread with hundreds of links then re-compared every link, re-resolved its current pull request, and rebuilt the saved summaries by re-parsing every link URL in the environment, on each tick from any thread. The shell reducer now keeps the prior links array when the links are unchanged, the current pull request is cached per links array, and saved summaries are cached per link.
The automated pipeline stopped on this pull request and will not merge it:
in the real app the problem is still there, and the rework changed nothing:
No code change: I found no defect. The report says the problem still happens but gives no numbers and names no wrong behavior. I checked the mechanism directly. A throwaway benchmark (not committed) used the proof's fixture shape (312 threads, 1,340 links, 322 on the streaming thread) and ran 200 thread.updated ticks, each carrying freshly copied links. On main the streaming row's links array was replaced on all 200 ticks. On this branch it was kept on all 200, and resolving the current PR for every row fell from about 430 ms to 14 ms; reducer cost was the same on both sides, about 95 ms. I traced the real app's paths. Stream deltas carry each thread's full row with its links (ShellStream) and go through applyShellStreamEvents. resolveThreadCurrentPullRequest is one shared instance for every web and mobile caller. threadShellsAtom and quietThreadShellAtomFamily reuse the source rows' links arrays. So the caches are reached in the real app. Main has not touched the involved files since the last merge, so no merge was needed. The PR never claimed to remove the streaming slowdown: its own profile showed main-thread busy time per streamed turn going from about 1,230 to 1,020 ms (decode, Schema equivalence and rendering remain). A total-busy-time comparison on a synthetic fixture with few shell ticks per turn can see that gain as noise. If the bar is removing the slowdown, that needs a separate, larger change, not a fix to this branch. Nothing was pushed, so I did not re-watch the PR; its checks had already passed on this head.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
size:M30-99 changed lines (additions + deletions).takeover:blockedTakeover blocked; reason in the PR bodyvouch:unvouchedAuthor is not yet trusted in the VOUCHED list.
2 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
clank context (clickme)
Follow-up to #37001 and #37002.
Problem
Streaming in a thread with many linked pull requests repeats work over every link on each shell tick. While a run streams, the server resends the thread's whole shell several times a second, and every linked pull request arrives as a freshly decoded object. Each tick then:
sameThreadShell,sameApartFromStreaming),SidebarThreadRow,ThreadStatusIndicators,threadReference,pullRequestDetail.logic),savedPullRequestSummariesAtomfrom every link in the environment. Each link's URL is parsed inthreadPullRequestLinkToSummary, and its key is normalized again becausesavedPullRequestKeywraps it in a fresh object, so theWeakMapkey cache misses. This atom rebuilds on any thread's tick, not just the streaming one.The thread this came from has 322 linked PRs; the environment has 1,340 links across 312 threads.
Change
shellReducer: athread.updateddelta, or a snapshot row, whose links equal the prior row's keeps the prior links array. Later equivalence checks then stop at identity, and per-link caches stay warm.resolveThreadCurrentPullRequest: cached per links array in aWeakMap(arrays are immutable).savedPullRequestSummariesAtom: keys by the link itself, so the existing key cache hits, and caches each link's summary per link object.Behavior is unchanged: a changed link yields a new array, so every cache recomputes.
Scope
A real performance problem for long-running threads that link many PRs (stacks, sweeps).
client-runtimeandsource-control-coreare shared with mobile, so the reducer and current-PR changes apply there too.savedPullRequestSummariesAtomis web-only.Verification
Real data, not fixtures: a copy of the actual 322-PR thread (22,900 items, 486-thread family) and its environment, made with
migrate-dev-db. The server and browser ran inside a sandbox (bubblewrap with no network, read-only filesystem, provider credentials hidden), so nothing could reach the real worktree or a provider. The app was a production build of currentmainwith sourcemaps. In Playwright I switched the thread's composer to theReplay (dev)Showcase model, streamed one turn, and captured a CDP trace with the V8 CPU profiler. Base and branch builds ran alternately, 3 runs each, with CPU profiles symbolicated through the build's sourcemaps:toEquivalence)SchemaParser)resolveThreadCurrentPullRequestsavedPullRequestSummariesAtomA reducer-only build reached about 1,150 ms busy and left the summaries rebuild at about 37 ms and current-PR resolution at about 35 ms. So all three changes carry weight.
shellReducer.test.tsgets one case: a changed row with equal links keeps the prior array on both the single and batched paths, and a changed link replaces it. It fails onmainand passes here.vp run check:changedpasses (19 packages typechecked).