fix(cluster): harden worker thread lifecycle - #6043
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughWorker threads now support explicit graceful shutdown. Utilities wait for cleanup or a timeout, then terminate workers when required. Sticky mode is rejected with worker-thread startup, and related protocol, integration, and documentation coverage was added. ChangesWorker-thread shutdown
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant ShutdownUtility
participant WorkerThread
participant CleanupHandler
ShutdownUtility->>WorkerThread: post WORKER_THREAD_GRACEFUL_EXIT
WorkerThread->>CleanupHandler: await beforeExit
CleanupHandler-->>WorkerThread: cleanup completes or fails
WorkerThread-->>ShutdownUtility: exit event
ShutdownUtility->>WorkerThread: terminate after timeout
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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-v3 with
|
| Latest commit: |
242d90b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://e959bda5.egg-v3.pages.dev |
| Branch Preview URL: | https://codex-cluster-worker-runtime.egg-v3.pages.dev |
Deploying egg with
|
| Latest commit: |
242d90b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://477ef7cc.egg-cci.pages.dev |
| Branch Preview URL: | https://codex-cluster-worker-runtime.egg-cci.pages.dev |
13ce99a to
0a81b31
Compare
bed5490 to
7ed8296
Compare
0a81b31 to
38128f3
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## next #6043 +/- ##
==========================================
+ Coverage 83.08% 83.43% +0.34%
==========================================
Files 730 730
Lines 22474 22500 +26
Branches 4520 4524 +4
==========================================
+ Hits 18673 18773 +100
+ Misses 3297 3226 -71
+ Partials 504 501 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR strengthens @eggjs/cluster shutdown behavior when running in worker_threads mode by switching to an explicit master→worker graceful-exit protocol (with a timeout and termination fallback), tightening invalid option combinations, and extending protocol coverage tests and docs.
Changes:
- Implement master-driven worker-thread graceful shutdown via a dedicated close message, waiting for app/agent cleanup and falling back to
Worker.terminate()on timeout. - Reject
sticky: truewhenstartMode: "worker_threads"(sticky handoff requires process IPC). - Add/extend tests and documentation for worker-thread shutdown and port/reusePort semantics (including server error cases).
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| wiki/packages/egg-bundler.md | Document worker-thread shutdown protocol and sticky-session limitation in worker_threads mode. |
| wiki/log.md | Update changelog note to reflect worker-thread graceful shutdown behavior. |
| packages/cluster/test/worker-thread-utils.test.ts | Add new unit tests for worker-thread kill/graceful-exit/terminate fallback behavior. |
| packages/cluster/test/worker-protocol.test.ts | Add protocol coverage for worker-thread cleanup and port/hostname/error scenarios. |
| packages/cluster/test/worker-protocol-io.test.ts | Extend IO adapter tests to cover graceful-exit message handling and failure path. |
| packages/cluster/test/options.test.ts | Add regression test ensuring sticky+worker_threads is rejected. |
| packages/cluster/test/fixtures/worker-thread-io.mjs | Fixture now registers gracefulExit to validate worker-thread cleanup sequencing. |
| packages/cluster/src/worker_protocol/worker-thread.ts | Introduce WORKER_THREAD_GRACEFUL_EXIT and implement message-triggered graceful exit. |
| packages/cluster/src/worker_protocol/app.ts | Clarify master-port 0 semantics and expand listening behaviors covered by tests. |
| packages/cluster/src/utils/options.ts | Enforce sticky-session incompatibility with worker_threads startMode. |
| packages/cluster/src/utils/mode/impl/worker_threads/app.ts | Implement graceful shutdown + timeout fallback for app workers in worker_threads mode. |
| packages/cluster/src/utils/mode/impl/worker_threads/agent.ts | Implement graceful shutdown + timeout fallback for agent worker in worker_threads mode. |
| const id = Reflect.get(worker, 'id'); | ||
| this.log(`[master] gracefully close app worker#${id} (worker_threads)`); | ||
| worker.removeAllListeners(); | ||
| const exited = once(worker, 'exit').then( | ||
| () => true, | ||
| (err) => { | ||
| this.logger.error('[master] app worker#%s error during graceful shutdown: ', id, err); | ||
| return false; | ||
| }, | ||
| ); | ||
| worker.postMessage(WORKER_THREAD_GRACEFUL_EXIT); | ||
| if (!(await Promise.race([exited, sleep(timeout).then(() => false)]))) { | ||
| this.log(`[master] terminate app worker#${id} after ${timeout}ms timeout`); | ||
| await worker.terminate(); | ||
| } |
| } | ||
| process.exit(0); | ||
| } catch (err) { | ||
| options.logger?.error('[worker_thread] graceful exit failed: %s', err); |
Summary
Hardens the
worker_threadscluster lifecycle on top of the bundle and startup-snapshot support merged in #6042:exitevent;Worker.terminate()after the configured timeout;errorevents during graceful shutdown, then continue with the termination fallback without exposing an unhandled EventEmitter error;sticky: truewithstartMode: worker_threads, because sticky socket handoff requires process IPC;reusePortlisten semantics, with focused server-error and hostname regression coverage.Why
A worker thread's
exitevent is emitted after the thread has already stopped, so cleanup cannot reliably be initiated from that event. The new protocol asks the worker to close while it is still alive and keeps forced termination as a bounded fallback.Sticky mode is rejected explicitly because its socket handoff mechanism is only available to process workers.
Validation
ut run test --workspace @eggjs/cluster -- test/worker-thread-utils.test.ts test/worker-protocol.test.ts test/worker-protocol-io.test.ts— 24 passedvitest run test/options.test.ts -t 'should reject sticky mode with worker_threads'— 1 passedut run typecheck --workspace @eggjs/cluster— passedThe full cross-platform suite is covered by CI.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation