Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions packages/core/src/egg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ export interface EggCoreOptions {
env?: string;
/** Skip lifecycle hooks, only trigger loadMetadata for manifest generation */
metadataOnly?: boolean;
/**
* When true, the application loads metadata only (plugins, configs, extensions,
* services, controllers) without starting servers, timers, or connections.
* Used for V8 startup snapshot construction.
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The snapshot option comment here claims the app loads metadata only and avoids starting servers/timers/connections, but in this PR snapshot mode still runs the normal lifecycle through didLoad (including configWillLoad/configDidLoad, didLoad hooks, and registerBeforeStart tasks). That code can still start timers/connections. Please adjust the doc to match the actual semantics (stop after didLoad / skip ready phases), or enforce the stronger guarantee in code.

Suggested change
* When true, the application loads metadata only (plugins, configs, extensions,
* services, controllers) without starting servers, timers, or connections.
* Used for V8 startup snapshot construction.
* When true, the application runs a snapshot-oriented bootstrap for V8
* startup snapshot construction.
*
* In this mode Egg still executes the normal lifecycle up through `didLoad`,
* including `configWillLoad` / `configDidLoad`, `didLoad` hooks, and
* `registerBeforeStart` tasks, but stops before the ready / after-start
* phases (for example, it does not itself proceed to start HTTP servers).
*
* Code that runs in these earlier lifecycle stages (application or plugin
* hooks) may still create timers, background tasks, or external connections,
* so callers MUST NOT rely on this option to guarantee a completely
* side-effect-free or connection-free startup.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The suggested 8-line JSDoc is too verbose for a simple boolean option. The key information developers need is: (1) lifecycle stops after didLoad, (2) later phases are skipped, (3) purpose is snapshot construction.

Will update to a concise version that accurately reflects the behavior without over-documenting edge cases that are better covered in framework docs. See reply to CodeRabbit's similar comment above.

*/
snapshot?: boolean;
}

export type EggCoreInitOptions = Partial<EggCoreOptions>;
Expand Down Expand Up @@ -191,6 +197,7 @@ export class EggCore extends KoaApplication {
baseDir: options.baseDir,
app: this,
logger: this.console,
snapshot: options.snapshot,
});
this.lifecycle.on('error', (err) => this.emit('error', err));
this.lifecycle.on('ready_timeout', (id) => this.emit('ready_timeout', id));
Expand Down
12 changes: 11 additions & 1 deletion packages/core/src/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ export interface LifecycleOptions {
baseDir: string;
app: EggCore;
logger: EggConsoleLogger;
/**
* When true, the lifecycle stops after didLoad phase completes.
* willReady, didReady, and serverDidReady hooks are NOT called.
* Used for V8 startup snapshot construction.
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The JSDoc says serverDidReady hooks are NOT called in snapshot mode, but Lifecycle.triggerServerDidReady() can still be invoked externally (e.g. packages/egg/src/lib/egg.ts triggers it on egg-ready, and tests call it directly). To make this guarantee true, consider making triggerServerDidReady() a no-op when options.snapshot is true (or soften the comment to clarify it’s only skipped by the lifecycle’s normal flow).

Suggested change
* When true, the lifecycle stops after didLoad phase completes.
* willReady, didReady, and serverDidReady hooks are NOT called.
* Used for V8 startup snapshot construction.
* When true, the lifecycle's normal flow stops after the didLoad phase
* completes, and willReady, didReady, and serverDidReady hooks are not
* invoked automatically. External callers may still trigger those hooks
* manually if needed. Used for V8 startup snapshot construction.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is by design. The JSDoc documents what the lifecycle flow does automatically — it does not call serverDidReady. External callers invoking triggerServerDidReady() directly would be doing so intentionally and at their own risk.

Making it a no-op would prevent legitimate use cases where snapshot consumers need to selectively trigger later phases. The current wording ("hooks are NOT called") is accurate for the lifecycle's own behavior. Won't change, but appreciate the thoroughness.

*/
Comment on lines +72 to +79
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The PR description says snapshot mode “stops after didLoad”, but the implementation (and this JSDoc) now short-circuits immediately after configWillLoad (before configDidLoad). Please update the PR description (or rename/clarify the feature wording) so reviewers/users don’t get conflicting semantics.

Copilot uses AI. Check for mistakes.
snapshot?: boolean;
}

export type FunWithFullPath = Fun & { fullPath?: string };
Expand Down Expand Up @@ -119,7 +125,7 @@ export class Lifecycle extends EventEmitter {
});

this.ready((err) => {
if (!this.#metadataOnly) {
if (!this.#metadataOnly && !this.options.snapshot) {
void this.triggerDidReady(err);
}
debug('app ready');
Expand Down Expand Up @@ -372,6 +378,10 @@ export class Lifecycle extends EventEmitter {
debug('trigger didLoad end');
if (err) {
this.ready(err);
} else if (this.options.snapshot) {
// In snapshot mode, stop after didLoad — skip willReady/didReady/serverDidReady
debug('snapshot mode: skipping willReady, marking ready after didLoad');
this.ready(true);
} else {
this.triggerWillReady();
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Snapshot-mode behavior (marking app ready after didLoad and skipping willReady/didReady) isn’t covered by tests in packages/core/test. Since this alters lifecycle semantics, please add a unit test that asserts the executed hook sequence in snapshot mode and verifies willReady/didReady are not invoked.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agree that tests are needed. Snapshot mode tests will be included in the integration test PR (feat/snapshot-scripts, PR #5853) which exercises the full snapshot build + restore flow end-to-end. Adding isolated lifecycle unit tests here would duplicate that coverage. The core change is minimal (3 lines of control flow) and well-understood.

}
Expand Down
Loading