Repository navigation
在没有 systemd 的主机上, fallbackOwner.waitForExit 会每 15ms 全表扫描一次 /proc
#12537
Replies: 4 comments
|
这条是真的: 链路对得上
所以你 strace 出来的"3 秒里主线程对同一个 fd 读了 5 万次"完全对得上:740 个进程 × 每秒约 66 次 = 每秒约 4.9 万次 stat 读。 为什么要扫全表 因为 这条路径什么时候跑 不是"有 systemd/没 systemd"本身触发的,而是 修法形状
只读了 master |
|
Adding a field report from a production container (no systemd, Docker), because the user-visible impact is worse than "UI feels slow". Environment: Impact on the Web GUI. With the fallback waiter spinning, Measurements (same host, before → after killing the leaked process group):
V8 CPU profile matched yours exactly: Root cause of the leak (no bug in What made it much worse in practice. The cold session catalog is expensive (1900 session dirs, and every list also recomputes +1 for the throttling/backoff fix in the waiter, and for not scanning the whole process table per tick. Happy to test a patch on this host. |
|
@PerryLink's trace is correct, and there's a specific reason the cost is unbounded per poll that's worth adding — plus a fix that costs nothing in correctness. The fast path can never engage for this workload. But look at the guard that reaches it: Three fixes, cheapest first:
I'd ship (1) and (2) together — (1) alone still leaves a 15 ms poll doing one On the container report from @sashatroshin: the reason |
|
One correction to the fix, since it changes where the poll loop should live.
Two fixes, and the second is the one I'd propose:
If only one lands, take (2) — (1) makes the symptom quieter without fixing the scaling, and on a 2100-process host the interval would have to go to seconds to matter, which breaks the responsiveness the 15 ms was chosen for. |
Uh oh!
There was an error while loading. Please reload this page.
下面是 DeepSeek V4.1 Flash 写的,大概率会很难看懂
环境
@deepseek-ai/dsh0.2.2-rc.1,Node 26.12.1症状
不操作 UI、也没有 agent 活动时,
dsh web的 host 进程长期占 44~52% CPU。Web UI 跟着发卡——每次输入都要排在这部分负载后面。重启 host 能短暂恢复,但只要还有子进程活着,压力很快回来。测量
strace -f -p <host> -e trace=read跑 5 秒:76,504 次read,其中 主线程在 3 秒内对同一个 fd 读了 50,593 次,内容全是/proc/<pid>/stat(连kworker/*这类内核线程都在读)。通过 inspector 采的 8 秒 V8 CPU profile,调用链是:
self time:
readFileUtf821.4%、parseProcStat8.6%、readdir4.0% —— 光这一条链就占了约 34% 的采样。根因
packages/subprocess/subprocess-local/lib/runner-launch-*.js:在拿不到 systemd scope 的主机上会选
fallbackOwner。直接子进程 settle 之后,每次alive()只要kill(-pid, 0)成功就会做一次全表扫描,而waitForExit每 15ms 调它一次。于是 N 个进程 → 每 15ms N 次文件读;736 个进程的机器上实测约 9,000 次 open/秒。建议的修法
process.kill(-pid, 0)已经能回答「进程组还在不在」;把linuxProcessGroupHasLiveMembers限制成每个 owner 每几百毫秒最多跑一次。/proc/<pid>/task/<tid>/children递归就够,根进程存活用pidfd,全表只留给「只剩僵尸」这个边角情况。本地 workaround
我把
sleepTick提到 100ms,并给进程组扫描加了 250ms 节流。同一台机器实测:host 空闲 CPU 从 46% 降到 2~3.5%,UI 延迟恢复正常。改动由一个幂等脚本在每次安装后自动重打。补丁 diff 可以随时提供——不过按 CONTRIBUTING 的说法目前不接受 PR,所以先发在这里。
Related: #12353 是同一模块在 systemd 路径上的对应问题(transient scope 累积把 user manager 拖满),我这边是无 systemd 时的
fallbackOwner路径。All reactions