Skip to content

demo(benchmark-react): add multi-view entity update scenario#3806

Merged
ntucker merged 3 commits intomasterfrom
bench-multi-view-entity
Mar 21, 2026
Merged

demo(benchmark-react): add multi-view entity update scenario#3806
ntucker merged 3 commits intomasterfrom
bench-multi-view-entity

Conversation

@ntucker
Copy link
Collaborator

@ntucker ntucker commented Mar 21, 2026

Motivation

The existing update-entity benchmark measures a single-view update — one list rendering the entity. Real apps display the same entity across multiple independent UI contexts simultaneously: a list row, a detail panel, and a bookmarks/pinned sidebar. This scenario tests how efficiently each library propagates a single entity change across all of those views.

Solution

Add update-entity-multi-view scenario that pre-mounts three structurally different component trees sharing issue #1:

  1. Issue list — standard IssueRow in a 1000-item list (100 rendered)
  2. Detail panelDetailView fetching issue TS2741: Property 'manager' is missing in type '{ children: Element; }' but required in type 'ProviderProps'. #1 via a separate get endpoint
  3. Pinned card strip — 10 PinnedCard components, each fetching individually by ID

An isReady predicate waits for the updated title to appear in all three views before stopping the timer.

Results (React Compiler on, network sim off):

Framework update-entity-multi-view
data-client 3.7 ms
TanStack Query 11.7 ms
SWR 11.8 ms

data-client updates the normalized entity once and all three component trees reflect the change automatically (~3 re-renders). TanStack/SWR must invalidate the list query plus all individual issue queries, causing all 100 list rows to re-render due to new array references.

Open questions

N/A

Made with Cursor


Note

Low Risk
Changes are limited to the examples/benchmark-react benchmark harness, scenarios, and docs; risk is mainly around benchmark correctness/consistency (timing/ready predicates) rather than production behavior.

Overview
Adds a new React benchmark scenario, update-entity-multi-view, that mounts a list + detail panel + pinned-card strip and measures how a single issue title update propagates across all three views.

Extends the shared harness (useBenchState) and BenchAPI with initMultiView/updateEntityMultiView, adds pinned-card UI (PinnedCardView, PINNED_STRIP_STYLE), and implements the new update action for data-client/SWR/TanStack (including invalidation/mutate behavior for non-normalized libs).

Updates benchmark docs/results tables and tweaks the runner by removing the extra forced GC after pre-mount (GC remains forced before each scenario).

Written by Cursor Bugbot for commit fe3b551. This will update automatically on new commits. Configure here.

Add `update-entity-multi-view` benchmark where the same issue entity
is displayed across three structurally different component trees (list
row, detail panel, pinned card strip). A single entity update must
propagate to all three views, exercising normalized cache cross-query
propagation vs. multi-query invalidation + refetch.

Made-with: Cursor
@changeset-bot
Copy link

changeset-bot bot commented Mar 21, 2026

⚠️ No Changeset found

Latest commit: fe3b551

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Mar 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs-site Ignored Ignored Preview Mar 21, 2026 7:42pm

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark React

Details
Benchmark suite Current: fe3b551 Previous: 9c22178 Ratio
data-client: getlist-100 15.4 ms (± 0.34) 17.2 ms (± 0.65) 0.90
data-client: getlist-500 41.3 ms (± 4.19) 40.9 ms (± 0.79) 1.01
data-client: update-entity 4.9 ms (± 0.19) 4.3 ms (± 0.27) 1.14
data-client: ref-stability-issue-changed 50 count (± 0) 50 count (± 0) 1
data-client: ref-stability-user-changed 50 count (± 0) 50 count (± 0) 1
data-client: update-user 5.4 ms (± 0.48) 6.4 ms (± 0.3) 0.84
data-client: getlist-500-sorted 43.1 ms (± 6.09) 42.7 ms (± 4.74) 1.01
data-client: update-entity-sorted 4.9 ms (± 0.35) 5.6 ms (± 0) 0.88
data-client: update-entity-multi-view 4.7 ms (± 0.47)
data-client: list-detail-switch 141.3 ms (± 25.07) 144.3 ms (± 22.87) 0.98
data-client: update-user-10000 44.1 ms (± 2.83) 32.5 ms (± 2.84) 1.36
data-client: invalidate-and-resolve 26.4 ms (± 0.19) 31.6 ms (± 0.71) 0.84
data-client: unshift-item 5.6 ms (± 0.29) 6.4 ms (± 0.32) 0.87
data-client: delete-item 5.7 ms (± 0.35) 5.3 ms (± 0.39) 1.08
data-client: move-item 6.9 ms (± 0.82) 9.7 ms (± 0.61) 0.71

This comment was automatically generated by workflow using github-action-benchmark.

@codecov
Copy link

codecov bot commented Mar 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.06%. Comparing base (9c22178) to head (fe3b551).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3806   +/-   ##
=======================================
  Coverage   98.06%   98.06%           
=======================================
  Files         151      151           
  Lines        2843     2843           
  Branches      556      556           
=======================================
  Hits         2788     2788           
  Misses         11       11           
  Partials       44       44           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Remove forced HeapProfiler.collectGarbage after pre-mount. The full GC
promoted all recently-allocated entities into V8's old generation,
causing write-barrier overhead during the timed action that
disproportionately penalized data-client's CPU-bound optimistic updates
(~1.8x inflation) while leaving network-bound libraries unaffected.

Also re-measure all scenarios and reorganize the README summary table
into Navigation / Mutations / Scaling categories.

Made-with: Cursor
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

performance.clearMarks();
performance.clearMeasures();
});
// Force GC after pre-mount so V8 doesn't collect during the timed action
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed post-pre-mount GC increases measurement variance

Medium Severity

The forced HeapProfiler.collectGarbage call after pre-mount was removed. Its own comment explained: "Force GC after pre-mount so V8 doesn't collect during the timed action." This affects all scenarios that use preMountAction (e.g., update-entity, update-entity-sorted, update-user, update-user-10000, ref-stability-*, move-item, and the new update-entity-multi-view). Without this GC, V8 may schedule garbage collection during the short timed measurement (~2–10 ms), adding noise to results.

Fix in Cursor Fix in Web

measureMount's MutationObserver called setComplete() (setting
data-bench-complete) as soon as list items appeared, before the
detail panel and pinned card views were ready. The runner could
see this premature signal, proceed to the timed update phase, and
then receive a stale second setComplete() — corrupting the
measurement.

Refactor measureMount to return a Promise and accept
{ signalComplete: false } so initMultiView can suppress the
early completion signal and call setComplete() once after all
three views are ready.

Made-with: Cursor
@ntucker ntucker merged commit 655b42e into master Mar 21, 2026
23 checks passed
@ntucker ntucker deleted the bench-multi-view-entity branch March 21, 2026 19:49
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