docs/plans/2026-07-31-daemon-memory-budget-pr1.md
日期:2026-07-31
设计文档:docs/design/2026-07-31-daemon-capacity-model-and-memory-bounds.md
关联:#8051(方向讨论)、#8182(缺陷);替代 #8093 的第一个 PR
把 daemon 的内存分母解析出来并如实上报,不改变任何子进程的启动参数。目的是让后续的 child-capacity 策略有一个可设计的依据,而不是自己再发明一个分母。
acp-bridge 新增 daemon-memory-budget.ts(纯算术,无 I/O,宿主内存可注入)产出 DaemonMemoryBudget。runQwenServe 在解析完 workspace 输入后调用一次并挂到 opts.daemonMemoryBudget,仅供状态面读取。daemon-status 把静态事实放 limits.memory、动态计数放 runtime.memory。spawnChannel.ts 完全不动。
insufficientMemory。--max-old-space-size 不是 RSS 边界(不含 Buffer / native / young gen / channel worker / MCP 子孙)。任何基于它的策略都是子进程堆策略。daemon_status 已是 baseline,新增可选字段不属于 CONDITIONAL_SERVE_FEATURES。computeEffectiveMemoryLimit() 是 MemoryPressureMonitor 的 private 方法,抽取会触发 maintainer 门禁。| 包 | 文件 | 改动 |
|---|---|---|
| acp-bridge | src/daemon-memory-budget.ts | 新增:DaemonMemoryBudget、resolver、legacyChildCeilingMb、recommendedChildShareMb |
| acp-bridge | src/daemon-memory-budget.test.ts | 新增:算术表、effective 封顶、insufficientMemory、建议份额上界 |
| acp-bridge | package.json | ./daemonMemoryBudget subpath export |
| acp-bridge | src/spawnChannel.ts | 不改动 |
| cli | src/commands/serve.ts | --memory-budget-mb 声明 + 范围校验 + 透传 |
| cli | src/serve/fast-path.ts | NUMBER_OPTIONS 一行(与 yargs 的 parity 有契约测试强制) |
| cli | src/serve/types.ts | memoryBudgetMb / daemonMemoryBudget;修订 maxSessions 文档段 |
| cli | src/serve/run-qwen-serve.ts | boot 解析 + 面包屑;bootstrap 占位状态补 memory: null |
| cli | src/serve/daemon-status.ts | limits.memory 静态事实 + runtime.memory 活跃计数与建议份额 |
| cli | 三个 *.test.ts | 预算上报、live 计数、flag 解析、boot 校验 |
| sdk | src/daemon/types.ts | DaemonStatusReport.limits.memory?(:610-624) |
| docs | daemon 17/20、users/qwen-serve.md、protocol | flag 行 + limits.memory / runtime.memory 字段说明 |
| 位置 | 用途 |
|---|---|
run-qwen-serve.ts:2326-2330 | 显式 workspace 上限校验 → 预算解析插在其后 |
run-qwen-serve.ts:~1541 | bootstrap 占位状态的第二处 DaemonStatusLimits |
run-qwen-serve.ts:481-492 | --mcp-client-budget 面包屑风格 |
daemon-status.ts:~365 | workspaceSnapshots → channelLive 即活跃子进程 |
commands/serve.ts ~:310 | flag 声明位(--mcp-client-budget 旁) |
availableMemoryMb = cgroup limit, else os.totalmem() (封顶在宿主总量)
configuredBudgetMb = --memory-budget-mb ?? floor(availableMemoryMb * 0.5)
effectiveBudgetMb = min(configuredBudgetMb, availableMemoryMb)
rootReserveMb = min(clamp(floor(effectiveBudgetMb * 0.1), 256, 1024), effectiveBudgetMb)
childPoolMb = effectiveBudgetMb - rootReserveMb
legacyChildCeilingMb = min(floor(availableMemoryMb * 0.5), 16384)
insufficientMemory = effectiveBudgetMb < 1024
recommendedChildShareMb(budget, n) = min(clamp(floor(childPoolMb / n), 512, 16384), legacyChildCeilingMb) —— 只上报,不应用。
daemon-memory-budget.ts + 单测package.json subpath exportfast-path.ts 一行 + ServeOptions 两个字段limits.memory / runtime.memory 与 SDK mirrorlint + typecheck + 目标单测全绿run-qwen-serve.test.ts 里真实启动 daemon 并 GET /daemon/status),断言 effective 不超过宿主、pool 小于 effective、runtime.memory 计数存在。真实输出确认了封顶:3.4 GB 机器上传 --memory-budget-mb 4096 降到 3494 并打出说明runtime.memory 镜像、极小宿主上保留额大于预算);最后一轮无发现/review(等 #8051 对字段集有反馈后再跑,避免字段还会变)spawnChannel.ts:27-34 的 raise-only 判断会把偏小的份额整个丢掉,回归测试必须断言低于测试进程自身堆上限的值仍会输出 flag。spawnChannel.ts 上一版曾加过一个可选 maxOldSpaceSizeMB 参数和 C1 回归测试,本 PR 已整体回退——没有消费者的参数就是未接线基础设施。该陷阱记录在设计文档和 #8182。