Skip to content

fix(core): Speed up Redux DevTools serialization of timestamps - #4163

Merged
ntucker merged 4 commits into
masterfrom
claude/project-thread-0i5n5k
Oct 5, 2026
Merged

ntucker merged 4 commits into
masterfrom
claude/project-thread-0i5n5k

Conversation

@ntucker

@ntucker ntucker commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Nathaniel · project thread

Before: with Redux DevTools open, every store update stalled the page in development. Serializing the store for DevTools took about 20ms with 50 entities and 200ms with 500.

After: each update serializes 40-60x faster, and timestamps still read as 10:42:07.123 AM.

The default serialize.replacer built a new Intl.DateTimeFormat for every date / *At number it formatted, and DevTools runs the replacer over the whole state on every action.

How: build the formatter once in DevToolsManager's dev-only module block and reuse it. Output is identical. The v0.19 blog gets a "Faster Redux DevTools" section with a PerfChart.

Measurement

JSON.stringify(state, replacer) per store update, median of 5 runs on Node 22. Both replacers produce byte-identical output.

Store Before After Speedup
50 entities 18.6 ms 0.31 ms 60x
500 entities 201 ms 5.0 ms 40x

Changeset

Fix slow updates in development while Redux DevTools is open

With the Redux DevTools extension open, every store update stalled the page while DevTools serialized the store: about 20ms with 50 entities, and 200ms with 500. Apps doing many `controller.set()` calls, polling, or live updates would stutter in development. Each update now serializes 40-60x faster, and timestamps still show as readable times like `10:42:07.123 AM`.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HJNAqsGkg5XHQYV9Bp5DXx


Note

Low Risk
Dev-only DevTools path with identical serialized output; no production or runtime store behavior changes.

Overview
Fixes development stutter when Redux DevTools is open by reusing a single Intl.DateTimeFormat for timestamp fields instead of constructing one on every date / *At value during DevTools state serialization.

DevtoolsManager's dev-only serialize.replacer now calls dateFormat.format(value) on a formatter created once at module load. Serialized output is unchanged (still readable times like 10:42:07.123 AM), but each store update serializes 40–60× faster on large or frequently updated stores. Production builds are unaffected.

Release notes: changeset plus v0.19 blog bullet and Faster Redux DevTools section with benchmark chart.

Reviewed by Cursor Bugbot for commit 3ea4fdb. Bugbot is set up for automated code reviews on this repo. Configure here.

…timestamp

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJNAqsGkg5XHQYV9Bp5DXx
@changeset-bot

changeset-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3ea4fdb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@data-client/core Patch
@data-client/react Patch
@data-client/vue Patch
example-benchmark Patch
example-benchmark-react Patch
test-bundlesize Patch
coinbase-lite Patch

Not sure what this means? Click here to learn what changesets are.

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

@vercel

vercel Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
docs-site Ready Ready Preview Oct 5, 2026 7:40am UTC

Request Review

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJNAqsGkg5XHQYV9Bp5DXx
@ntucker ntucker self-assigned this Oct 4, 2026

@ntucker ntucker left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Staff engineer (Cursor agent): LGTM. Hoisting the Intl.DateTimeFormat into the dev-only module block is the simplest correct fix: same options, same output, and the typeof Intl guard now doubles as the replacer switch, so there's one less flag. Unknown options like fractionalSecondDigits are ignored rather than thrown by older engines, so moving construction to module load doesn't add a new failure point. No changes requested for this PR.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 103 kB

ℹ️ View Unchanged
Filename Size
examples/test-bundlesize/dist/App.js 1.46 kB
examples/test-bundlesize/dist/polyfill.js 307 B
examples/test-bundlesize/dist/rdcClient.js 10.9 kB
examples/test-bundlesize/dist/rdcEndpoint.js 8.07 kB
examples/test-bundlesize/dist/rdcNextjs.js 12.3 kB
examples/test-bundlesize/dist/rdcPipeableStream.js 9.64 kB
examples/test-bundlesize/dist/react.js 59.7 kB
examples/test-bundlesize/dist/webpack-runtime.js 784 B

compressed-size-action

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown
Contributor

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: 3ea4fdb Previous: b239883 Ratio
data-client: getlist-100 136.06 ops/s (± 4.2%) 175.44 ops/s (± 3.7%) 1.29
data-client: getlist-500 42.46 ops/s (± 3.5%) 55.71 ops/s (± 4.0%) 1.31
data-client: update-entity 339.08 ops/s (± 7.9%) 454.55 ops/s (± 8.4%) 1.34
data-client: update-user 307.77 ops/s (± 5.3%) 454.55 ops/s (± 9.3%) 1.48
data-client: getlist-500-sorted 42.64 ops/s (± 9.4%) 56.03 ops/s (± 9.8%) 1.31
data-client: update-entity-sorted 266.71 ops/s (± 7.2%) 408.33 ops/s (± 7.1%) 1.53
data-client: update-entity-multi-view 303.03 ops/s (± 7.6%) 416.67 ops/s (± 8.6%) 1.38
data-client: list-detail-switch-10 8.34 ops/s (± 9.6%) 14.71 ops/s (± 7.7%) 1.76
data-client: update-user-10000 71.43 ops/s (± 15.7%) 85.84 ops/s (± 16.4%) 1.20
data-client: invalidate-and-resolve 36.17 ops/s (± 5.3%) 47.96 ops/s (± 5.5%) 1.33
data-client: unshift-item 200 ops/s (± 6.4%) 263.16 ops/s (± 6.1%) 1.32
data-client: delete-item 270.27 ops/s (± 4.8%) 357.14 ops/s (± 4.7%) 1.32
data-client: move-item 168.08 ops/s (± 10.0%) 212.77 ops/s (± 8.3%) 1.27

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

@codecov

codecov Bot commented Oct 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.06%. Comparing base (b5db637) to head (025e4e4).
⚠️ Report is 8 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4163   +/-   ##
=======================================
  Coverage   98.06%   98.06%           
=======================================
  Files         163      163           
  Lines        3095     3095           
  Branches      616      616           
=======================================
  Hits         3035     3035           
  Misses         18       18           
  Partials       42       42           

☔ View full report in Codecov by Harness.
📢 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.

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benchmark

Details
Benchmark suite Current: 3ea4fdb Previous: b239883 Ratio
normalizeLong 778 ops/sec (±5.85%) 427 ops/sec (±3.13%) 0.55
normalizeLong Values 694 ops/sec (±0.24%) 387 ops/sec (±0.86%) 0.56
normalizeLong Scalar 602 ops/sec (±2.84%) 359 ops/sec (±3.70%) 0.60
normalizeLong Scalar update 1787 ops/sec (±0.29%) 921 ops/sec (±0.19%) 0.52
denormalizeLong 396 ops/sec (±6.25%) 243 ops/sec (±6.27%) 0.61
denormalizeLong Values 363 ops/sec (±3.68%) 224 ops/sec (±5.37%) 0.62
denormalizeLong donotcache 1976 ops/sec (±0.78%) 1000 ops/sec (±0.80%) 0.51
denormalizeLong Values donotcache 1378 ops/sec (±0.38%) 749 ops/sec (±0.20%) 0.54
denormalizeLong Scalar donotcache 2152 ops/sec (±0.25%) 1063 ops/sec (±0.17%) 0.49
denormalizeShort donotcache 500x 2491 ops/sec (±2.61%) 1402 ops/sec (±0.12%) 0.56
denormalizeShort 500x 1001 ops/sec (±6.61%) 637 ops/sec (±6.57%) 0.64
denormalizeShort 500x withCache 10293 ops/sec (±0.24%) 7080 ops/sec (±0.20%) 0.69
queryShort 500x withCache 5899 ops/sec (±0.23%) 3193 ops/sec (±0.12%) 0.54
buildQueryKey All 97421 ops/sec (±0.51%) 58045 ops/sec (±0.68%) 0.60
query All withCache 10778 ops/sec (±5.80%) 6282 ops/sec (±2.69%) 0.58
denormalizeLong with mixin Entity 350 ops/sec (±6.68%) 221 ops/sec (±7.08%) 0.63
denormalizeLong withCache 14234 ops/sec (±0.58%) 7616 ops/sec (±0.17%) 0.54
denormalizeLong withCache (Scalar churn) 14275 ops/sec (±0.96%) 7618 ops/sec (±0.20%) 0.53
denormalizeLong Values withCache 10654 ops/sec (±1.31%) 5205 ops/sec (±1.58%) 0.49
denormalizeLong Scalar withCache 14840 ops/sec (±0.40%) 7839 ops/sec (±0.39%) 0.53
denormalizeLong Scalar update withCache 7993 ops/sec (±0.39%) 4075 ops/sec (±0.28%) 0.51
denormalizeLong All withCache 13832 ops/sec (±0.44%) 6710 ops/sec (±0.61%) 0.49
denormalizeLong Query-sorted withCache 10659 ops/sec (±6.82%) 6598 ops/sec (±1.48%) 0.62
denormalizeLongAndShort withEntityCacheOnly 2992 ops/sec (±4.39%) 1813 ops/sec (±0.26%) 0.61
denormalize bidirectional 50 7506 ops/sec (±9.90%) 4537 ops/sec (±10.97%) 0.60
denormalize bidirectional 50 donotcache 73682 ops/sec (±3.86%) 42714 ops/sec (±0.48%) 0.58
getResponse 7723 ops/sec (±5.30%) 4440 ops/sec (±3.68%) 0.57
getResponse (null) 20779493 ops/sec (±0.53%) 9674787 ops/sec (±0.75%) 0.47
getResponse (clear cache) 323 ops/sec (±7.83%) 208 ops/sec (±9.15%) 0.64
getSmallResponse 6352 ops/sec (±1.22%) 3447 ops/sec (±2.98%) 0.54
getSmallInferredResponse 4960 ops/sec (±1.13%) 2886 ops/sec (±1.37%) 0.58
getResponse Collection 7952 ops/sec (±3.80%) 4361 ops/sec (±4.43%) 0.55
get Collection 5832 ops/sec (±0.43%) 2691 ops/sec (±0.40%) 0.46
get Query-sorted 8886 ops/sec (±4.01%) 5053 ops/sec (±1.85%) 0.57
setLong 808 ops/sec (±0.50%) 434 ops/sec (±0.26%) 0.54
setLongWithMerge 451 ops/sec (±0.47%) 250 ops/sec (±0.18%) 0.55
setLongWithSimpleMerge 475 ops/sec (±0.36%) 268 ops/sec (±0.67%) 0.56
setSmallResponse 500x 1644 ops/sec (±2.90%) 906 ops/sec (±1.17%) 0.55
setMany 50x one-per-row 226 ops/sec (±1.39%) 145 ops/sec (±1.04%) 0.64
setMany 50 batch 6233 ops/sec (±1.61%) 3515 ops/sec (±2.80%) 0.56
setMany 500x one-per-row 23.05 ops/sec (±0.47%) 13.98 ops/sec (±0.90%) 0.61
setMany 500 batch 2870 ops/sec (±0.34%) 1382 ops/sec (±3.26%) 0.48

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

@ntucker ntucker left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewed 025e4e4ad365e20c09ccc24af3f9dc068ccc456f. No actionable correctness findings. The shared formatter preserves the replacer's field/value filters and stays inside the development-only block. Independently checked old/new output parity in Node for seven timestamps, including negative and Date-range boundary values. Validation is limited to serializer output and source review; I did not run the browser DevTools integration or reproduce the performance benchmark.

ntucker commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

Sol review bot (GPT-6.1 Sol, High): I posted this review of commit 025e4e4ad365.

claude added 2 commits October 5, 2026 07:28
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJNAqsGkg5XHQYV9Bp5DXx
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJNAqsGkg5XHQYV9Bp5DXx
@ntucker
ntucker merged commit 159c963 into master Oct 5, 2026
27 checks passed
@ntucker
ntucker deleted the claude/project-thread-0i5n5k branch October 5, 2026 08:12
@github-actions github-actions Bot mentioned this pull request Oct 5, 2026

This branch was successfully deployed

1 active deployment
Preview — 3ea4fdbc Deployed Oct 5, 2026 by vercel[bot]
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