Skip to content

Warm the file cache on a background worker thread#223

Open
freshlogic wants to merge 8 commits into
mainfrom
claude/lazy-file-read-background-SjTlo
Open

Warm the file cache on a background worker thread#223
freshlogic wants to merge 8 commits into
mainfrom
claude/lazy-file-read-background-SjTlo

Conversation

@freshlogic
Copy link
Copy Markdown
Member

The first request for an uncached file read and processed it
synchronously (Sass, Babel, Snockets, UglifyJS, gzip), blocking the
event loop and slowing the whole site. Extract the processing pipeline
into a shared processor module and run it in a background worker thread
that lazily reads every file in the directory, posting finished cache
entries back to the main thread. The synchronous read stays as the
per-request fallback, so behavior is unchanged for any file not yet
warmed. Warming is skipped while watching (it registers discovered
dependencies on the main thread) and when options carry functions
(which can't be cloned into the worker), to keep cached output identical
to the synchronous path.

https://claude.ai/code/session_013nWBHBH1uYqZ35q6TTks99

claude added 4 commits May 23, 2026 06:20
The first request for an uncached file read and processed it
synchronously (Sass, Babel, Snockets, UglifyJS, gzip), blocking the
event loop and slowing the whole site. Extract the processing pipeline
into a shared processor module and run it in a background worker thread
that lazily reads every file in the directory, posting finished cache
entries back to the main thread. The synchronous read stays as the
per-request fallback, so behavior is unchanged for any file not yet
warmed. Warming is skipped while watching (it registers discovered
dependencies on the main thread) and when options carry functions
(which can't be cloned into the worker), to keep cached output identical
to the synchronous path.

https://claude.ai/code/session_013nWBHBH1uYqZ35q6TTks99
Spawning one worker per electricity.static() instance loaded the heavy
transform libraries (Babel/Sass/UglifyJS) once per instance and competed
for CPU. Use a single lazily-created worker that all instances submit
warm jobs to, tagged by job id so each instance's cache receives its own
entries.

https://claude.ai/code/session_013nWBHBH1uYqZ35q6TTks99
Replace the single warm worker with a shared pool sized to
max(1, cpus - 1), leaving a core free for the event loop. Each warm job
is fanned out across the pool, with every worker processing a disjoint,
deterministic shard of the directory's files so a single directory warms
in parallel. Each worker keeps its own processor, so a CSS file's url()
dependencies still resolve within that worker even when the referenced
asset lives in another shard; deterministic hashing keeps every entry
byte-identical to a synchronous read.

https://claude.ai/code/session_013nWBHBH1uYqZ35q6TTks99
Use os.availableParallelism() instead of os.cpus().length so the pool
respects the process's CPU affinity mask (e.g. cpuset pinning) rather
than always counting every host core.

https://claude.ai/code/session_013nWBHBH1uYqZ35q6TTks99
@coveralls
Copy link
Copy Markdown

coveralls commented May 23, 2026

Coverage Report for CI Build 26346126154

Coverage decreased (-2.0%) to 98.041%

Details

  • Coverage decreased (-2.0%) from the base build.
  • Patch coverage: 14 uncovered changes across 2 files (571 of 585 lines covered, 97.61%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
lib/index.js 145 133 91.72%
lib/warmer.js 98 96 97.96%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 772
Covered Lines: 758
Line Coverage: 98.19%
Relevant Branches: 147
Covered Branches: 143
Branch Coverage: 97.28%
Branches in Coverage %: Yes
Coverage Strength: 299.43 hits per line

💛 - Coveralls

claude added 4 commits May 23, 2026 14:49
Replace the bespoke hasFunction scan with a try/catch around the worker
postMessage. Options that can't be structured-cloned (e.g. a Sass
importer or a Babel plugin passed as a function) now skip warming for
that instance via the caught DataCloneError, which is simpler and covers
any non-cloneable value rather than only functions.

https://claude.ai/code/session_013nWBHBH1uYqZ35q6TTks99
Match the org convention used elsewhere (e.g. exports.static in
index.js): attach public functions to exports directly instead of a
trailing module.exports block. Internal references go through exports.X
since named function expressions aren't bound in module scope.

https://claude.ai/code/session_013nWBHBH1uYqZ35q6TTks99
Cover the startWarmer DataCloneError path: passing a Sass importer (a
function) must not crash static() — warming is skipped for that instance
and the synchronous read still serves the file.

https://claude.ai/code/session_013nWBHBH1uYqZ35q6TTks99
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.

3 participants