Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion packages/core/src/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,22 @@ export class Lifecycle extends EventEmitter {

/**
* Trigger snapshotWillSerialize on all boots in REVERSE order.
* Called by the build script before V8 serializes the heap.
*
* This is a general-purpose "clean up non-serializable resources" hook
* intended to run before a heap serialization mechanism captures application
* state. Each boot can implement `snapshotWillSerialize` to close file
* descriptors, drop process listeners, or release other resources that
* would otherwise prevent the heap from being serialized cleanly.
*
* NOTE: There is currently no caller that drives this hook under
* `node --build-snapshot`. A previous `buildSnapshot()` facade was removed
* because Node 22's mksnapshot blocks userland `require()`
* (MODULE_NOT_FOUND on any non-builtin) and rejects heap state containing
* non-zero async_hooks stacks — and egg's loader inherently creates async
* contexts that the event loop drain inside `SpinEventLoopInternal` cannot
* flush to depth 0. The hook API is kept as a stable abstraction for a
* future non-mksnapshot serialization mechanism (e.g. forked-worker
* snapshots) that does not have the same restrictions.
*/
async triggerSnapshotWillSerialize(): Promise<void> {
if (!this.options.snapshot) {
Expand Down
3 changes: 0 additions & 3 deletions packages/egg/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ export type {

export * from './lib/start.ts';

// export snapshot utilities
export * from './lib/snapshot.ts';

// export singleton
export { Singleton, type SingletonCreateMethod, type SingletonOptions } from '@eggjs/core';

Expand Down
90 changes: 0 additions & 90 deletions packages/egg/src/lib/snapshot.ts

This file was deleted.

2 changes: 0 additions & 2 deletions packages/egg/test/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,10 @@ exports[`should expose properties 1`] = `
"Singleton": [Function],
"SingletonProto": [Function],
"Subscription": [Function],
"buildSnapshot": [Function],
"createTransparentProxy": [Function],
"defineConfig": [Function],
"defineConfigFactory": [Function],
"definePluginFactory": [Function],
"restoreSnapshot": [Function],
"start": [Function],
"startCluster": [Function],
"startEgg": [Function],
Expand Down
19 changes: 9 additions & 10 deletions packages/egg/test/snapshot.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
// These tests exercise the snapshotWillSerialize / snapshotDidDeserialize
// lifecycle hooks under a normal Node.js process — NOT under
// `node --build-snapshot`. The facade that would invoke these hooks from a
// real V8 snapshot build (`buildSnapshot`/`restoreSnapshot`) was removed
// because it could not work under Node's mksnapshot constraints: userland
// `require()` is blocked (MODULE_NOT_FOUND), and egg's async-heavy loader
// corrupts the async_hooks stack before `SpinEventLoopInternal` can finish.
// The hooks remain as a general-purpose resource-cleanup abstraction that
// a future non-mksnapshot serialization mechanism could drive.
import { strict as assert } from 'node:assert';
import path from 'node:path';

import { describe, it, afterEach } from 'vitest';

import { Agent } from '../src/lib/agent.ts';
import { Application } from '../src/lib/application.ts';
import { restoreSnapshot } from '../src/lib/snapshot.ts';
import { startEgg } from '../src/lib/start.ts';

const fixtures = path.join(import.meta.dirname, 'fixtures');
Expand Down Expand Up @@ -216,13 +224,4 @@ describe('test/snapshot.test.ts', () => {
await app.agent.close();
});
});

describe('restoreSnapshot', () => {
it('should throw when no snapshot app exists', async () => {
// Ensure no global snapshot app
globalThis.__egg_snapshot_app = undefined;

await assert.rejects(() => restoreSnapshot(), /No egg application found in snapshot/);
});
});
});
Loading