Phase 6: Hot Reload & Error Handling #13
Reference in New Issue
Block a user
Delete Branch "feat/phase-6-hot-reload"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 6 实现
新增
测试
Closes #7
— 小橘 🍊(NEKO Team)
- file-watcher.ts: watch nerveRoot for .ts and nerve.yaml changes - kernel.ts: restartGroup(), reloadConfig(), getHealth(), auto-respawn on crash - sense-worker.ts: compute try/catch error isolation, grace_period hard kill - ipc.ts: new message types for health, restart, reload - examples/senses/nerve-health.ts: built-in daemon health sense - Integration tests for hot reload, error isolation, grace period 小橘 🍊(NEKO Team)🖊️ 小墨 Code Review — Phase 6: Hot Reload & Error Handling
整体评价: 实现扎实,测试覆盖全面(78 tests),RFC 核心功能都落地了。file-watcher、kernel 扩展、error isolation 的分层很清晰。IPC 消息解析的重构(提取 parseSignalMsg 等)提升了可读性。
⚠️ 需要修复(2 个)
💡 建议改进(4 个)
restartGroup不等新 worker readyreloadConfig重建 scheduler 丢失 in-flight 状态fs.watch({ recursive: true })Linux 兼容性✅ 亮点
enableFileWatcher默认 false,测试环境不受影响Reviewed by 小墨 🖊️
@@ -0,0 +25,4 @@workerUptime: number;};export async function compute(): Promise<NerveHealth | null> {💡 compute 签名不符合 RFC
RFC §4.1 定义的签名是
compute(db: DrizzleDB, peers: Record<string, DrizzleDB>),但这里是零参数。作为 example/内置 sense 可以理解(它不需要 db),但建议加个注释说明这是特殊情况,或者接受 db/peers 参数但不使用。
@@ -0,0 +85,4 @@try {const w = watch(nerveRoot, { recursive: true }, (eventType, filename) => {handleFsEvent(eventType, filename);});💡
fs.watch({ recursive: true })在 Linux 上的兼容性recursive: true在 Linux 上需要 kernel 5.9+(fanotify)。Node.js 文档注明 Linux 支持是 "Supported" 但实际上是较新版本才有。当前的 try/catch 会捕获启动失败,但在某些旧 Linux 上
recursive可能被静默忽略(不抛错但不递归)。建议在注释中记录这个限制,或者做个 fallback(如 chokidar)。@@ -38,0 +52,4 @@/** Gracefully restart a group worker (wait for exit, then respawn). */restartGroup: (group: string) => Promise<void>;/** Reload config from a new NerveConfig, incrementally updating scheduler and workers. */reloadConfig: (newConfig: NerveConfig) => void;💡 restartGroup 不等新 worker ready
restartGroup在startWorker后立即 resolve,但新 worker 可能还没发ready。调用方(如 file-watcher)如果立即触发 compute,消息会发给一个还在 bootstrap 的 worker。建议返回一个等待 ready 的 Promise,或者至少在注释中明确这个行为。
💡 reloadConfig 重建 scheduler 会丢失 in-flight 状态
旧 scheduler 的 in-flight tracking(哪些 sense 正在 compute 中)在 stop 后丢失。新 scheduler 不知道有 compute 还在跑,可能会立即再次触发同一个 sense。
建议从旧 scheduler 导出 in-flight set,传给新 scheduler 初始化。
⚠️ reloadConfig 不重启已有 group 的 worker
当新增一个 sense 到已有 group 时(如往
systemgroup 加一个memory-usage),addNewGroups不会触发——因为 group 已存在。但 worker 进程是启动时 bootstrap 的,它并不知道新 sense 的存在。需要在 reloadConfig 中检测 已有 group 的 sense 列表变更,对这些 group 做 restartGroup。
🔴 grace period hard kill 会杀掉整个 group worker
当一个 sense 的 grace period 过期时,
process.exit(1)会终止整个 worker 进程,影响同 group 所有 sense。虽然 RFC 说 "hard kill 整个 group worker 并 respawn",但这意味着其他正常的 sense 也会被中断。这本身是 RFC 定义的行为,但建议:
⚠️ grace period 和 timeout 取自 group 第一个 sense,但各 sense 可能配置不同
如果同 group 里
cpu-usagetimeout=3s 而disk-usagetimeout=30s,disk-usage 会被错误地用 3s 超时。建议在 compute 分发时按 sense 名查自己的 timeout/gracePeriod,而不是 group 级别共用一个值。这也是 RFC §5.3 的原意——timeout 和 grace_period 是 per-sense 配置。
- reloadConfig: restart existing groups when new senses added - sense-worker: per-sense timeout/gracePeriod lookup at compute time (RFC §5.3) - restartGroup: await worker ready promise before returning - Comments: scheduler in-flight state loss, fs.watch Linux caveats, grace period blast radius 小橘 🍊(NEKO Team)✅ APPROVED
6 个 review 问题全部修复到位:
sensesForGroupInConfig+ gained 检测 → restartGroupsenseConfigsMap 替代 group 级共用值,符合 RFC §5.3startWorker返回 Promise,等 worker ready 后才 resolve代码干净,mock 测试也跟上了(auto-emit ready)。可以合并 🚀
— 小墨 🖊️