Skip to content

fix: hand out a recorded hash update as buffers - #289

Merged
alexander-akait merged 2 commits into
mainfrom
fix/cached-hash-update-buffer
Sep 24, 2026
Merged

alexander-akait merged 2 commits into
mainfrom
fix/cached-hash-update-buffer

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Summary

Fixes a regression 3.5.2 (#286) introduced in webpack's persistent cache.

CachedSource.updateHash records what the wrapped source hashed so later calls replay it. Before 3.5.2 a string-backed source hashed its utf8 buffer, so the recording held a Buffer; since 3.5.2 it hashes the string directly, so the recording holds that string — and getCachedData() handed it straight to callers that serialize it. webpack's pack serializer then warns Serializing big strings (1922kiB) impacts deserialization performance and pays that cost on every deserialization; it turns webpack's configCases/css/large red under ConfigCacheTestCases (e.g. webpack/webpack#22299).

The fix encodes those strings as buffers in getCachedData() — the boundary a caller serializes from — so the recording still holds the source's own string and nothing is copied until it is written out. 3.5.2's memory win is kept: no buffer is materialized while hashing, only when cached data is handed out. Hashing reads a string as utf8, so a restored update hashes to the same digest.

What kind of change does this PR introduce?

fix

Did you add tests for your changes?

Yes — test/CachedSource.js asserts getCachedData().hash holds no string, that the recording still keeps the source's own string uncopied, and that a CachedSource restored from that data hashes to the same digest. It fails without the change.

Verified against webpack: with this build, configCases/css/large passes and the whole ConfigCacheTestCases suite emits no big-string warning (only the two profiling-plugin cases fail, for unrelated sandbox reasons).

Does this PR introduce a breaking change?

No. The on-disk shape returned by getCachedData() goes back to what 3.5.1 produced.

If relevant, what needs to be documented once your changes are merged or what have you already documented?

n/a

Use of AI

AI was used to root-cause the regression from webpack's failing cache suite, write the test and the fix, and verify both suites; the result was reviewed in-session.


Generated by Claude Code

Summary by CodeRabbit

  • Improvements
    • Reduced memory overhead when caching source data with large text while preserving hash results.
    • Reusing cached data continues to produce consistent hashes, including when restoring a source from the cache.

CachedSource.updateHash records what the wrapped source hashed so later
calls replay it. Since 3.5.2 a string-backed source hashes its string, so
the recording keeps that string, and getCachedData() handed it to callers
that serialize it — webpack's pack warns "Serializing big strings" and
pays for it on every deserialization.

Encode the strings as buffers in getCachedData(), the boundary a caller
serializes from, so the recording still holds the source's own string and
nothing copies it until it is written out. Hashing reads a string as utf8,
so the restored update hashes to the same digest.
@changeset-bot

changeset-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9e47885

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

This PR includes changesets to release 1 package
Name Type
webpack-sources 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

@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: b71a51ac-ccc1-4f8a-9979-97596ff7898c

📥 Commits

Reviewing files that changed from the base of the PR and between 6f9c00e and 9e47885.

📒 Files selected for processing (3)
  • .changeset/cached-hash-update-buffer.md
  • lib/CachedSource.js
  • test/CachedSource.js

Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review.


Walkthrough

When getCachedData() is called, recorded string entries in the hash update are converted in place to UTF-8 buffers. Tests cover large-string updates, repeated cached-data retrieval, and matching hashes after restoring a source from cached data.

Merge Risk: ⚪ Minimal · up to 9e478

The change stores cached hash updates as UTF-8 buffers, which the supported hash implementations treat equivalently to strings. No material merge risk remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: returning recorded hash updates as buffers to avoid exposing large strings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Warning

Some tools did not complete. Review the errors below.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

lib/CachedSource.js

ESLint failed to execute (timeout).

test/CachedSource.js

ESLint skipped: the matched ESLint configuration already failed (timeout).


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.30%. Comparing base (c22a0e3) to head (9e47885).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #289   +/-   ##
=======================================
  Coverage   98.29%   98.30%           
=======================================
  Files          25       25           
  Lines        2289     2295    +6     
  Branches      735      738    +3     
=======================================
+ Hits         2250     2256    +6     
  Misses         37       37           
  Partials        2        2           
Flag Coverage Δ
integration 98.30% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@codspeed

codspeed Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 31.45%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 8 improved benchmarks
❌ 27 regressed benchmarks
✅ 177 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Memory source-map-source memory: new SourceMapSource(simple) 3 KB 193.3 KB -98.45%
❌ Memory source-map-source memory: new SourceMapSource(with inner map) 1.3 KB 41.8 KB -96.93%
❌ Memory size-only-source memory: new SizeOnlySource() 95.9 KB 898.9 KB -89.33%
❌ Memory original-source memory: map({ columns: false }) line-only mappings 117.9 KB 420 KB -71.92%
❌ Memory original-source memory: map({ columns: true }) builds full mappings 468.9 KB 1,451.6 KB -67.7%
❌ Memory replace-source memory: construct + 100 insertions 36.9 KB 74.1 KB -50.21%
❌ Memory cached-source memory: getCachedData() allocates BufferedMap 848 B 1,384 B -38.73%
❌ Memory concat-source memory: new ConcatSource(...children) 3.1 KB 4.9 KB -37.38%
❌ Simulation helpers/stringBufferUtils: internString (enabled) 139 µs 204.3 µs -31.96%
❌ Simulation original-source: new OriginalSource(string) 175.5 µs 240.3 µs -26.96%
❌ Simulation original-source: source() 193.4 µs 257 µs -24.75%
❌ Simulation original-source: new OriginalSource(buffer) 176.4 µs 229.4 µs -23.11%
❌ Simulation original-source: buffers() (from buffer) 209.8 µs 272.5 µs -23.01%
❌ Simulation original-source: getName() 253.6 µs 312.8 µs -18.91%
❌ Memory clear-cache memory: unique tasks (clearCache maps + parsedMap, keep source) 3.9 MB 4.7 MB -17.99%
❌ Simulation raw-source: new RawSource(buffer) 213.5 µs 256.1 µs -16.64%
❌ Simulation raw-source: new RawSource(buffer, true) 213.6 µs 255.4 µs -16.36%
❌ Simulation raw-source: new RawSource(string) 217.8 µs 259.8 µs -16.16%
❌ Simulation raw-source: source() (string) 235.5 µs 277.6 µs -15.19%
❌ Simulation raw-source: buffer() (from buffer) 232 µs 273.3 µs -15.11%
... ... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/cached-hash-update-buffer (9e47885) with main (6f9c00e)

Open in CodSpeed

The first version rebuilt the buffered array on every getCachedData()
call, so a source serialized more than once was re-encoded each time:
+19.7MB of buffer allocation over the css/large fixture's cache build.

Encode in place instead. A source nobody asks cached data of still keeps
its own string uncopied, and one that is handed out repeatedly is encoded
once.
@alexander-akait
alexander-akait merged commit 1c97070 into main Sep 24, 2026
36 of 37 checks passed
@alexander-akait
alexander-akait deleted the fix/cached-hash-update-buffer branch September 24, 2026 18:27
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