fix(chv-muse-r12fix3-1): worker 开机时序竞态:Condition + path 触发,不赌重启预算(P6 缺口 ①)

R12 复测 2 钉死:剩下的 226 只点名 /home/chv/workspace——工作区由
cloud-final 的 chv-sync boot 克隆出来,晚于 multi-user.target 约
9 秒,worker 的 5 次启动预算在工作区出现前耗尽(5 台 VM 2 台
failed,手工 reset-failed + start 后立即 active,二进制无辜)。

修法三选一,选 (a)——让服务根本不在工作区出现前启动:
新单元 chv-gitea-worker.path(PathExists=/home/chv/workspace,
工作区一出现即触发启动)+ 服务加
ConditionPathExists=/home/chv/workspace(开机那次启动在命名空间
搭建之前干净跳过,不 226、不耗重启预算)。两者缺一不可:
Condition 本身绝不重试,没有 .path 它就是一次跳过、服务永远不
起;只有 .path 而无 Condition,开机那次启动仍会在命名空间搭建
时 226 撞上限速。WantedBy=multi-user.target 保留(is-enabled 仍
为 enabled),runcmd 只启动 .path 单元(cloud-final 时工作区尚
未 clone,直接 start 服务会 226);.path 进 buildinfo contents
与两处 lockfile 登记,P6 预检由三件扩为四件。

不用 (b) 放宽 StartLimit:放宽只是把竞态窗口拉大,wake 时间在
负载下已翻倍(R12 复测 2),预算仍可能耗尽,仍是时序赌博。
不用 (c) 把工作区创建提前到 tmpfiles-setup 之前:/home/chv 本身
07:23:40 才出现(cloud-init 建 chv 用户),晚于 tmpfiles-setup
(07:23:24);硬约束:绝不盲目补 tmpfiles 行。ExecStartPre 也不
行——命名空间搭建在它之前,救不了 226。

测试:WorkerUnitBootRaceTest 新增 8 项——Condition 在位、
StartLimit 未放宽(修法语义钉)、.path 触发配对(PathExists +
WantedBy、无 Unit= 覆盖)、服务 WantedBy 保留、build.sh 启用
.path + runcmd 只起 .path、绝无工作区 tmpfiles 行、buildinfo 与
lockfile 登记;BuildInfoContentsTest 的 P6 件加 .path;P2/P4 干
跑夹具一并拷入 .path。P6/P2/P4 三模块 174 项全过。
This commit is contained in:
host committed 2026-11-08 09:45:48 +00:00
1 parent 7c517f7fcc
commit e31354dc58
10 files changed
+165 -15

No files matched your search

+10 -4
View File
@@ -216,8 +216,8 @@ if [ ! -f "$SCRIPT_DIR/chv-memory.py" ]; then
echo "缺少 chv-memory.py(客内记忆文件脚本):$SCRIPT_DIR" >&2
exit 1
fi
# 对齐 Muse P6:privsep worker / 薄客户端 / 单元三件缺一即失败。
for p6_file in chv-gitea-worker.py chv-sync-worker-client.py chv-gitea-worker.service; do
# 对齐 Muse P6:privsep worker / 薄客户端 / 单元 / 路径触发器四件缺一即失败。
for p6_file in chv-gitea-worker.py chv-sync-worker-client.py chv-gitea-worker.service chv-gitea-worker.path; do
if [ ! -f "$SCRIPT_DIR/$p6_file" ]; then
echo "缺少 $p6_file(Muse P6 客机文件):$SCRIPT_DIR" >&2
exit 1
@@ -347,7 +347,12 @@ VC_ARGS=(-a "$OUT/disk.raw"
--copy-in "$SCRIPT_DIR/chv-authd.service:/etc/systemd/system/"
--run-command 'mv /usr/local/bin/authd.py /usr/local/bin/chv-authd && chmod +x /usr/local/bin/chv-authd /usr/local/bin/git-credential-chv-authd'
--run-command 'systemctl enable chv-authd || true'
--run-command 'printf "runcmd:\n - systemctl restart ssh || service ssh restart || true\n - systemctl start chv-authd || true\n - test -f /etc/chv/guest-env && chown chv:chv /etc/chv/guest-env || true\n - systemctl start chv-gitea-worker || true\n - /usr/local/bin/chv-sync boot >>/var/log/chv-sync.log 2>&1 || true\n" > /etc/cloud/cloud.cfg.d/99-chv-sync.cfg'
# round-12 修复轮第三轮(缺口 ①):runcmd 只启动 .path 单元
#(开机时它已由 enable 激活,这里是兜底),绝不直接 start 服务
#——cloud-final 此时工作区尚未 clone,直接 start 服务会在命名
# 空间搭建时 226;.path 单元在 boot 的 mkdir -p 建出工作区后
# 才触发服务启动。
--run-command 'printf "runcmd:\n - systemctl restart ssh || service ssh restart || true\n - systemctl start chv-authd || true\n - test -f /etc/chv/guest-env && chown chv:chv /etc/chv/guest-env || true\n - systemctl start chv-gitea-worker.path || true\n - /usr/local/bin/chv-sync boot >>/var/log/chv-sync.log 2>&1 || true\n" > /etc/cloud/cloud.cfg.d/99-chv-sync.cfg'
--copy-in "$SCRIPT_DIR/chv-cdp-broker.py:/usr/local/bin/"
--copy-in "$SCRIPT_DIR/chv_browser_guard.py:/usr/local/bin/"
# round-12 修复轮(项 0):c3bf9fd 认领——browser-blocklist.txt
@@ -365,8 +370,9 @@ VC_ARGS=(-a "$OUT/disk.raw"
--copy-in "$SCRIPT_DIR/chv-gitea-worker.py:/usr/local/bin/"
--copy-in "$SCRIPT_DIR/chv-sync-worker-client.py:/usr/local/bin/"
--copy-in "$SCRIPT_DIR/chv-gitea-worker.service:/etc/systemd/system/"
--copy-in "$SCRIPT_DIR/chv-gitea-worker.path:/etc/systemd/system/"
--run-command 'mv /usr/local/bin/chv-gitea-worker.py /usr/local/bin/chv-gitea-worker && mv /usr/local/bin/chv-sync-worker-client.py /usr/local/bin/chv-sync-worker-client && chmod +x /usr/local/bin/chv-gitea-worker /usr/local/bin/chv-sync-worker-client'
--run-command 'systemctl enable chv-gitea-worker || true'
--run-command 'systemctl enable chv-gitea-worker chv-gitea-worker.path || true'
# round-11 真机修复(层 5):镜像不再设 credential.helper store
#(authd helper 是唯一凭证 helper);P4 的 copy-in 行保留。
+4 -2
View File
@@ -48,9 +48,11 @@ BASE_CONTENTS = [
# 对齐 Muse P4:单元外 CDP broker + 浏览器安全共享模块 +
# 静态拦截名单。
"chv-cdp-broker", "chv-browser-guard", "browser-blocklist",
# 对齐 Muse P6:privsep worker + 薄客户端 + 单元(无条件拷入)。
# 对齐 Muse P6:privsep worker + 薄客户端 + 单元 + 路径触发器
#(无条件拷入)。路径触发器是 round-12 修复轮第三轮缺口 ①
# 的拉起机制(工作区出现才启动服务),与单元同款登记。
"chv-gitea-worker", "chv-sync-worker-client",
"chv-gitea-worker.service",
"chv-gitea-worker.service", "chv-gitea-worker.path",
]
+2 -1
View File
@@ -49,10 +49,11 @@ DECLARED_SOURCES = {
"chv-cdp-broker": "repo_commit",
"chv-browser-guard": "repo_commit",
"browser-blocklist": "repo_commit",
# 对齐 Muse P6:worker 三件 + 叶子模块(privsep/safety)。
# 对齐 Muse P6:worker 四件(+ 路径触发器)+ 叶子模块(privsep/safety)。
"chv-gitea-worker": "repo_commit",
"chv-sync-worker-client": "repo_commit",
"chv-gitea-worker.service": "repo_commit",
"chv-gitea-worker.path": "repo_commit",
"cloud/chv/privsep.py": "repo_commit",
"cloud/chv/checkout_detect.py": "repo_commit",
"cloud/chv/safety.py": "repo_commit",
+20
View File
@@ -0,0 +1,20 @@
[Unit]
Description=Start chv-gitea-worker once the sync workspace exists (feat-chv-muse-p6)
# round-12 修复轮第三轮(缺口 ①):worker 的 ReadWritePaths 含
# /home/chv/workspace,systemd 建 mount namespace 时该路径必须已
# 存在;工作区由 cloud-final 的 chv-sync boot 克隆出来,晚于
# multi-user.target 约 9 秒(R12 复测 2:worker 07:23:31 首启
# 226,工作区 07:23:40.40 才出现,5 次启动预算同一秒内耗尽)。
# 本单元只在工作区出现时触发服务启动——服务绝不在工作区出现前
# 启动,命名空间搭建前即有保证。须与服务的 ConditionPathExists
# 配对:Condition 挡住开机那次过早启动(干净跳过、不 226),本
# 单元提供 Condition 本身没有的“出现后再试一次”。
# 不用 StartLimit 放宽(仍是时序赌博,wake 时间在负载下已翻倍,
# 预算仍可能耗尽)与 tmpfiles 补行(/home/chv 本身晚于
# tmpfiles-setup,补了也没用),见服务单元 [Unit] 注释。
[Path]
PathExists=/home/chv/workspace
[Install]
WantedBy=multi-user.target
+18 -2
View File
@@ -4,6 +4,20 @@ After=network-online.target chv-authd.service
Wants=network-online.target
# authd 是可选依赖:worker 的 git 经 P3 helper 向 authd 取凭证;
# authd 没起来时 worker 的 git 调用直接拒绝,不静默用旧凭证。
# 开机时序(round-12 修复轮第三轮,缺口 ①):工作区
# /home/chv/workspace 由 cloud-final 的 chv-sync boot 克隆出来,
# 晚于 multi-user.target 约 9 秒;而 ReadWritePaths 的路径必须在
# systemd 建 mount namespace 时已存在,否则 226/NAMESPACE。
# ConditionPathExists 让开机那次启动在工作区出现前干净跳过
# (不耗重启预算、不 226);真正拉起靠 chv-gitea-worker.path
# (工作区一出现即触发启动)。Condition 本身绝不重试——没有
# .path 单元它就是一次跳过、服务永远不起;反过来只有 .path 而
# 无 Condition,开机那次启动仍会在命名空间搭建时 226 撞上限速。
# 两者缺一不可。StartLimit 不放宽:放宽只是把竞态窗口拉大,
# wake 时间在负载下已翻倍(R12 复测 2),预算仍可能耗尽;
# tmpfiles.d 也不补工作区行:/home/chv 本身 07:23:40 才出现,
# 晚于 tmpfiles-setup(07:23:24),补了也没用。
ConditionPathExists=/home/chv/workspace
[Service]
Type=simple
@@ -28,8 +42,10 @@ SystemCallFilter=@system-service
# 下 systemd 建 mount namespace 的硬要求,缺一个即 226/NAMESPACE):
# /run/chv-gitea 与 /var/log/chv-gitea-worker.log 由
# /etc/tmpfiles.d/chv.conf 预建(build.sh 内联写,round-12 修复轮
# 第二轮项 1);/home/chv/workspace 开机时已在(R12 复测的 226
# 只报过前两处)。ExecStartPre 不能替代 tmpfiles.d——命名空间
# 第二轮项 1);/home/chv/workspace 由 ConditionPathExists +
# chv-gitea-worker.path 守门(round-12 修复轮第三轮缺口 ①,见
# [Unit];R12 复测 2 钉死它才是剩下的 226——开机时序竞态,不要
# 再写“开机时已在”)。ExecStartPre 不能替代 tmpfiles.d——命名空间
# 搭建在它之前,226 发生在它运行之前。
ReadWritePaths=/home/chv/workspace /run/chv-gitea /var/log/chv-gitea-worker.log
# worker 崩溃不拖垮 agent:broker/薄 CLI 收到类型化失败即返回,
+2 -1
View File
@@ -71,10 +71,11 @@ DECLARED_SOURCES = {
"chv-cdp-broker": "repo_commit",
"chv-browser-guard": "repo_commit",
"browser-blocklist": "repo_commit",
# 对齐 Muse P6:worker 三件 + 叶子模块(privsep/safety)。
# 对齐 Muse P6:worker 四件(+ 路径触发器)+ 叶子模块(privsep/safety)。
"chv-gitea-worker": "repo_commit",
"chv-sync-worker-client": "repo_commit",
"chv-gitea-worker.service": "repo_commit",
"chv-gitea-worker.path": "repo_commit",
"cloud/chv/privsep.py": "repo_commit",
"cloud/chv/checkout_detect.py": "repo_commit",
"cloud/chv/safety.py": "repo_commit",