feat(core): add NODE_COMPILE_CACHE support to manifest system#5855
feat(core): add NODE_COMPILE_CACHE support to manifest system#5855
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds Node.js compile-cache management: enables compile cache early in startup scripts and egg start, exposes enable/flush/clean methods on ManifestStore, flushes cache during ready/shutdown and removes cache on manifest clean, plus tests and a fixture exercising the cache. Changes
Sequence Diagram(s)sequenceDiagram
participant Start as Starter (script / master)
participant Egg as Egg runtime
participant Manifest as ManifestStore
participant NodeMod as node:module
participant FS as FileSystem
Start->>NodeMod: check enableCompileCache & env vars
alt NODE_COMPILE_CACHE not set and not disabled
Start->>FS: compute <base>/.egg/compile-cache
Start->>Start: set NODE_COMPILE_CACHE, NODE_COMPILE_CACHE_PORTABLE
Start->>NodeMod: enableCompileCache(cacheDir)
end
Start->>Egg: import/launch framework
Egg->>Manifest: enableCompileCache(baseDir) before write
Egg->>Manifest: write manifest
Egg->>Manifest: flushCompileCache()
Note right of FS: compile-cache files created on require()
Egg->>Manifest: flushCompileCache() on shutdown
Egg->>Manifest: clean() -> cleanCompileCache(baseDir) (removes dir)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Deploying egg with
|
| Latest commit: |
13dabf4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8a169e9d.egg-cci.pages.dev |
| Branch Preview URL: | https://mixed-manifest.egg-cci.pages.dev |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## next #5855 +/- ##
==========================================
- Coverage 85.27% 85.26% -0.02%
==========================================
Files 666 666
Lines 13210 13234 +24
Branches 1532 1534 +2
==========================================
+ Hits 11265 11284 +19
- Misses 1814 1819 +5
Partials 131 131 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Deploying egg-v3 with
|
| Latest commit: |
13dabf4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://7c8097e4.egg-v3.pages.dev |
| Branch Preview URL: | https://mixed-manifest.egg-v3.pages.dev |
There was a problem hiding this comment.
Code Review
This pull request introduces support for Node.js module compile cache and V8 startup snapshots to improve application startup performance. It adds utility methods to ManifestStore for managing the compile cache, integrates cache flushing into the application lifecycle, and updates the module loader to handle pre-loaded modules from a snapshot registry. Additionally, new scripts and tests are provided to support snapshot building and verify the compile cache functionality. Feedback was provided regarding the robustness of enabling the compile cache, suggesting error handling to prevent startup crashes.
There was a problem hiding this comment.
Pull request overview
Adds Node.js compile cache integration to Egg’s manifest/loader ecosystem to persist V8 bytecode under .egg/compile-cache/, aiming to reduce subsequent startup time. It also extends snapshot-build compatibility by adjusting module loading behavior to avoid dynamic import pitfalls.
Changes:
- Add compile-cache enable/flush/clean APIs to
ManifestStoreand wire compile-cache flushing into app ready + shutdown. - Enable compile cache in key runtime entry points (cluster master, start scripts, programmatic
startEgg()). - Add snapshot-oriented module import behavior (snapshot registry + CJS bundle override) with dedicated tests, plus a snapshot build script.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/scripts/scripts/start-single.mjs | New single-mode start entry; enables compile cache and manually starts HTTP server. |
| tools/scripts/scripts/start-cluster.mjs | Enables compile cache before loading framework startCluster (ESM). |
| tools/scripts/scripts/start-cluster.cjs | Enables compile cache before loading framework startCluster (CJS). |
| tools/scripts/scripts/build-snapshot.cjs | New snapshot build entry that disables compile cache and configures V8 snapshot restore behavior. |
| packages/utils/src/import.ts | Adds snapshot module registry support and snapshot CJS-bundle override to avoid dynamic import issues. |
| packages/utils/test/snapshot-import.test.ts | Adds unit tests covering snapshot registry and CJS-bundle override behaviors. |
| packages/egg/src/lib/start.ts | Enables compile cache for programmatic startEgg() (single mode). |
| packages/egg/src/lib/egg.ts | Flushes compile cache after ready and during graceful shutdown. |
| packages/core/src/loader/manifest.ts | Adds ManifestStore compile-cache APIs and cleans compile cache as part of clean(). |
| packages/core/test/loader/manifest.test.ts | Adds tests verifying compile cache enable/flush/clean behaviors. |
| packages/core/test/fixtures/compile-cache-target/index.cjs | Fixture module used to generate compile cache entries during tests. |
| packages/cluster/src/master.ts | Enables compile cache in cluster master so env propagates to forked workers. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
tools/scripts/scripts/build-snapshot.cjs (1)
41-41: Consider making the default port configurable or using a constant.The hardcoded default port
7001is duplicated across multiple files. Consider extracting this to a shared constant for consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tools/scripts/scripts/build-snapshot.cjs` at line 41, The default port value 7001 is hardcoded in the options parsing (const port = options.port || 7001;) — extract that magic number into a shared constant (e.g., DEFAULT_PORT) and use that constant instead of the literal; add the constant to your shared config/constants module (or a top-level constants file) and import or require it into build-snapshot.cjs, then replace the inline 7001 in the port assignment with the imported DEFAULT_PORT to keep defaults consistent across files (mentioning the symbols: options.port, port, DEFAULT_PORT).packages/cluster/src/master.ts (1)
62-63: Consider extracting compile cache enablement to@eggjs/utils.The compile cache enablement logic is duplicated in
packages/cluster/src/master.ts,tools/scripts/scripts/start-cluster.cjs, andpackages/core/src/loader/manifest.ts. Since@eggjs/utilsis already a dependency of@eggjs/cluster, consider adding a sharedenableCompileCachefunction there to reduce duplication.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cluster/src/master.ts` around lines 62 - 63, Extract the duplicated compile-cache enablement logic into a single exported utility function named enableCompileCache in `@eggjs/utils`, then replace the inline logic in packages/cluster/src/master.ts (the current comment block), tools/scripts/scripts/start-cluster.cjs, and packages/core/src/loader/manifest.ts with an import and call to that new enableCompileCache function; ensure the new utility preserves existing behavior (env propagation for forked workers) and export signature so callers (e.g., the master bootstrap and ManifestStore.enableCompileCache replacement) can call it without changing call sites.packages/utils/test/snapshot-import.test.ts (1)
46-50: Consider strengthening the fallthrough assertion.The assertion
assert.ok(result !== undefined)is weak. Consider verifying a known property from the actualesmfixture to confirm the real module was loaded.♻️ Stronger assertion example
// Should still load the real module (fall through) const filepath = getFilepath('esm'); const result = await importModule(filepath); - assert.ok(result !== undefined); + assert.ok(result !== undefined, 'should load real module when not in registry'); + // Optionally verify a known export from the esm fixture + assert.ok('default' in result || Object.keys(result).length > 0);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/utils/test/snapshot-import.test.ts` around lines 46 - 50, The current fallthrough assertion in snapshot-import.test.ts is too weak (assert.ok(result !== undefined)); update the test that calls getFilepath('esm') and importModule(filepath) to assert a specific known export/property from the real esm fixture (e.g., check result.default or result.someExportName equals the expected value) so the test verifies the actual module was loaded; locate the test using getFilepath and importModule and replace the undefined-check with a concrete equality or deep-equal assertion against the expected fixture value.packages/core/src/loader/manifest.ts (1)
236-245: Behavior change:clean()now removes compile-cache directory.This extends the contract of
clean()to also remove the compile-cache directory. Callers usingManifestStore.clean()(e.g.,egg-bin manifest --clean, manifest generation scripts) will now lose accumulated compile-cache files.This is likely intentional per PR objectives, but worth noting as a silent behavior change for existing consumers. Consider documenting this in the JSDoc for
clean().📝 Optional: Update JSDoc for clean()
+ /** + * Remove the manifest file and compile cache directory. + */ static clean(baseDir: string): void {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/loader/manifest.ts` around lines 236 - 245, Update the JSDoc for the static method Manifest.clean to document the new behavior: it not only removes the .egg/manifest.json file but also deletes the compile-cache directory via ManifestStore.cleanCompileCache(baseDir); mention that callers (e.g., egg-bin manifest --clean and manifest generation scripts) will lose accumulated compile-cache files and include parameters and side-effects in the docblock for clean(baseDir: string).packages/core/test/loader/manifest.test.ts (1)
556-570: Environment variable save/restore looks correct.The save/restore pattern properly handles both defined and undefined states. However, consider moving the capture into
beforeEachrather than at describe-scope to ensure isolation if other test suites modify these env vars:♻️ Optional: Capture in beforeEach for stronger isolation
describe('compile cache', () => { - const savedCompileCache = process.env.NODE_COMPILE_CACHE; - const savedPortable = process.env.NODE_COMPILE_CACHE_PORTABLE; + let savedCompileCache: string | undefined; + let savedPortable: string | undefined; + + beforeEach(() => { + savedCompileCache = process.env.NODE_COMPILE_CACHE; + savedPortable = process.env.NODE_COMPILE_CACHE_PORTABLE; + // Clear to ensure clean state for each test + delete process.env.NODE_COMPILE_CACHE; + delete process.env.NODE_COMPILE_CACHE_PORTABLE; + }); afterEach(() => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/test/loader/manifest.test.ts` around lines 556 - 570, The savedCompileCache and savedPortable variables are captured at describe-scope which can lead to cross-test-suite interference; change their declaration to mutable (use let savedCompileCache and let savedPortable) and assign their current values inside a beforeEach hook so each test captures fresh environment state, keeping the existing afterEach restore logic intact and referencing the same identifiers (savedCompileCache, savedPortable, beforeEach, afterEach) to ensure proper isolation.tools/scripts/scripts/start-single.mjs (1)
36-41: Consider includingportin the IPC message for consistency with cluster mode.The cluster master (see
packages/cluster/src/master.ts) sends bothportandaddressin theegg-readymessage data. The parent process listener (tools/scripts/src/commands/start.ts:270-278) only usesaddresscurrently, but addingportwould maintain consistency and provide the data if needed in the future.♻️ Optional: Add port to message
if (process.send) { process.send({ action: 'egg-ready', - data: { address: `http://127.0.0.1:${port}` }, + data: { port, address: `http://127.0.0.1:${port}` }, }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tools/scripts/scripts/start-single.mjs` around lines 36 - 41, The IPC message sent in the process.send block for action 'egg-ready' should include the numeric port in its data payload to match cluster mode; update the object sent by process.send(...) to include port: port alongside address (e.g., data: { address: `http://127.0.0.1:${port}`, port }), leaving the action name 'egg-ready' unchanged so parent listeners (which currently use address) can also access port if needed in the future.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tools/scripts/scripts/start-single.mjs`:
- Around line 23-28: The code may call an undefined startFn (resolved from
importModule(options.framework) via framework.start or framework.default?.start)
causing a cryptic TypeError; before invoking startFn, validate that startFn is a
function and if not throw or log a clear Error that includes the framework
identifier (options.framework) and guidance that the module must export a start
function (check framework.start and framework.default.start), then bail (throw
or process.exit) so callers get a helpful message instead of an unhelpful
TypeError.
---
Nitpick comments:
In `@packages/cluster/src/master.ts`:
- Around line 62-63: Extract the duplicated compile-cache enablement logic into
a single exported utility function named enableCompileCache in `@eggjs/utils`,
then replace the inline logic in packages/cluster/src/master.ts (the current
comment block), tools/scripts/scripts/start-cluster.cjs, and
packages/core/src/loader/manifest.ts with an import and call to that new
enableCompileCache function; ensure the new utility preserves existing behavior
(env propagation for forked workers) and export signature so callers (e.g., the
master bootstrap and ManifestStore.enableCompileCache replacement) can call it
without changing call sites.
In `@packages/core/src/loader/manifest.ts`:
- Around line 236-245: Update the JSDoc for the static method Manifest.clean to
document the new behavior: it not only removes the .egg/manifest.json file but
also deletes the compile-cache directory via
ManifestStore.cleanCompileCache(baseDir); mention that callers (e.g., egg-bin
manifest --clean and manifest generation scripts) will lose accumulated
compile-cache files and include parameters and side-effects in the docblock for
clean(baseDir: string).
In `@packages/core/test/loader/manifest.test.ts`:
- Around line 556-570: The savedCompileCache and savedPortable variables are
captured at describe-scope which can lead to cross-test-suite interference;
change their declaration to mutable (use let savedCompileCache and let
savedPortable) and assign their current values inside a beforeEach hook so each
test captures fresh environment state, keeping the existing afterEach restore
logic intact and referencing the same identifiers (savedCompileCache,
savedPortable, beforeEach, afterEach) to ensure proper isolation.
In `@packages/utils/test/snapshot-import.test.ts`:
- Around line 46-50: The current fallthrough assertion in
snapshot-import.test.ts is too weak (assert.ok(result !== undefined)); update
the test that calls getFilepath('esm') and importModule(filepath) to assert a
specific known export/property from the real esm fixture (e.g., check
result.default or result.someExportName equals the expected value) so the test
verifies the actual module was loaded; locate the test using getFilepath and
importModule and replace the undefined-check with a concrete equality or
deep-equal assertion against the expected fixture value.
In `@tools/scripts/scripts/build-snapshot.cjs`:
- Line 41: The default port value 7001 is hardcoded in the options parsing
(const port = options.port || 7001;) — extract that magic number into a shared
constant (e.g., DEFAULT_PORT) and use that constant instead of the literal; add
the constant to your shared config/constants module (or a top-level constants
file) and import or require it into build-snapshot.cjs, then replace the inline
7001 in the port assignment with the imported DEFAULT_PORT to keep defaults
consistent across files (mentioning the symbols: options.port, port,
DEFAULT_PORT).
In `@tools/scripts/scripts/start-single.mjs`:
- Around line 36-41: The IPC message sent in the process.send block for action
'egg-ready' should include the numeric port in its data payload to match cluster
mode; update the object sent by process.send(...) to include port: port
alongside address (e.g., data: { address: `http://127.0.0.1:${port}`, port }),
leaving the action name 'egg-ready' unchanged so parent listeners (which
currently use address) can also access port if needed in the future.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2d2c7ae9-e37d-4898-86a9-f314e720edb3
📒 Files selected for processing (12)
packages/cluster/src/master.tspackages/core/src/loader/manifest.tspackages/core/test/fixtures/compile-cache-target/index.cjspackages/core/test/loader/manifest.test.tspackages/egg/src/lib/egg.tspackages/egg/src/lib/start.tspackages/utils/src/import.tspackages/utils/test/snapshot-import.test.tstools/scripts/scripts/build-snapshot.cjstools/scripts/scripts/start-cluster.cjstools/scripts/scripts/start-cluster.mjstools/scripts/scripts/start-single.mjs
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tools/scripts/scripts/start-single.mjs`:
- Around line 37-40: The egg-ready message uses data.address instead of the
expected data.port, causing a contract mismatch; update the process.send call
that emits { action: 'egg-ready', data: { address: `http://127.0.0.1:${port}` }
} to instead send { action: 'egg-ready', data: { port } } so it matches the
sibling cluster ready messages and the parent-side ready handling; locate the
process.send invocation emitting 'egg-ready' and replace the payload
accordingly, preserving the existing port variable.
- Around line 16-21: Wrap the compile-cache setup in a try-catch in
start-single.mjs (and mirror the same change in start-cluster.mjs and
start-cluster.cjs): the block that computes cacheDir (using options.baseDir ??
process.cwd()), sets process.env.NODE_COMPILE_CACHE and
process.env.NODE_COMPILE_CACHE_PORTABLE, and calls
module.enableCompileCache?.(cacheDir) should be enclosed in a try { ... } catch
(err) { /* log or ignore */ } to prevent thrown errors from aborting startup;
ensure you catch the thrown error from module.enableCompileCache and handle it
consistently with the existing pattern used in
packages/core/src/loader/manifest.ts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 47d8def3-69e0-4918-892a-59af9d7fe6e8
📒 Files selected for processing (10)
packages/cluster/src/master.tspackages/core/src/loader/manifest.tspackages/core/test/fixtures/compile-cache-target/index.cjspackages/core/test/loader/manifest.test.tspackages/egg/src/lib/egg.tspackages/egg/src/lib/start.tstools/scripts/scripts/build-snapshot.cjstools/scripts/scripts/start-cluster.cjstools/scripts/scripts/start-cluster.mjstools/scripts/scripts/start-single.mjs
✅ Files skipped from review due to trivial changes (4)
- packages/core/test/fixtures/compile-cache-target/index.cjs
- tools/scripts/scripts/start-cluster.mjs
- packages/cluster/src/master.ts
- tools/scripts/scripts/start-cluster.cjs
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/egg/src/lib/start.ts
- packages/core/test/loader/manifest.test.ts
- tools/scripts/scripts/build-snapshot.cjs
| debug('start cluster options: %o', options); | ||
|
|
||
| // Duplicated from ManifestStore.enableCompileCache (@eggjs/core is not a dependency) | ||
| if (!process.env.NODE_COMPILE_CACHE && !process.env.NODE_DISABLE_COMPILE_CACHE) { |
There was a problem hiding this comment.
这个判断要抽到一个统一的util里去么?看起来好几处都有
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Enable Node.js module compile cache (v22.8.0+) across all entry points to cache compiled V8 bytecode to `.egg/compile-cache/`, improving subsequent startup performance. Uses NODE_COMPILE_CACHE_PORTABLE=1 for deployment portability. - Add enableCompileCache/flushCompileCache/cleanCompileCache to ManifestStore - Enable compile cache in cluster master (propagates to forked workers) - Enable compile cache in start-cluster.mjs/cjs entry scripts - Enable compile cache in programmatic startEgg() API - Flush compile cache on app ready and graceful shutdown - Respect NODE_DISABLE_COMPILE_CACHE opt-out - Clean compile cache when manifest is cleaned Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| - name: cnpmcore | ||
| node-version: 24 | ||
| command: | | ||
| npm install | ||
| npm run lint -- --quiet | ||
| npm install --legacy-peer-deps | ||
| npm run typecheck | ||
| npm run build | ||
| npm run prepublishOnly |
There was a problem hiding this comment.
The cnpmcore e2e command no longer runs npm run lint and also switches install behavior to npm install --legacy-peer-deps. This reduces CI signal (lint regressions won’t be caught here) and the change isn’t described in the PR summary. Please either re-add the lint step (after install) or add an explicit note in the PR description explaining why lint must be skipped and why --legacy-peer-deps is required for this job.
Summary
NODE_COMPILE_CACHE+NODE_COMPILE_CACHE_PORTABLEsupport to the manifest system, caching compiled V8 bytecode to.egg/compile-cache/for faster subsequent startupsenableCompileCache()/flushCompileCache()/cleanCompileCache()static methods toManifestStorestartEgg()APITest plan
ManifestStore.enableCompileCache()sets env vars and activates compile cacheflushCompileCache()does not throwcleanCompileCache()removes the cache directoryclean()removes both manifest.json and compile-cache directory@eggjs/core,egg,@eggjs/cluster🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests