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:
1 parent
7c517f7fcc
commit
e31354dc58
10 files changed
+165
-15
No files matched your search
@@ -633,7 +633,10 @@ class BuildDryRunTest(unittest.TestCase):
|
||||
# round-12 修复轮(项 2,P6):worker 三件亦
|
||||
# 是 build.sh 硬校验,夹具须一并拷入。
|
||||
"chv-gitea-worker.py", "chv-sync-worker-client.py",
|
||||
"chv-gitea-worker.service"):
|
||||
"chv-gitea-worker.service",
|
||||
# round-12 修复轮第三轮(缺口 ①):.path 单元
|
||||
# 亦是 P6 预检四件之一,夹具须一并拷入。
|
||||
"chv-gitea-worker.path"):
|
||||
shutil.copy2(ROOT / "chv-guest" / name,
|
||||
os.path.join(guest, name))
|
||||
shutil.copy2(ROOT / "host.py", os.path.join(repo, "host.py"))
|
||||
@@ -715,6 +718,7 @@ class BuildDryRunTest(unittest.TestCase):
|
||||
# 三件进镜像,干跑输出须含拷入与 enable。
|
||||
"chv-gitea-worker",
|
||||
"chv-gitea-worker.service",
|
||||
"chv-gitea-worker.path",
|
||||
"systemctl enable chv-gitea-worker",
|
||||
# round-12 修复轮(项 4,P10):结账检测
|
||||
# 模块进镜像,干跑输出须含条件拷入行。
|
||||
|
||||
@@ -859,7 +859,10 @@ class BuildDryRunTest(unittest.TestCase):
|
||||
# 是 build.sh 硬校验,夹具须一并拷入。
|
||||
"chv-gitea-worker.py",
|
||||
"chv-sync-worker-client.py",
|
||||
"chv-gitea-worker.service"):
|
||||
"chv-gitea-worker.service",
|
||||
# round-12 修复轮第三轮(缺口 ①):.path
|
||||
# 单元亦是 P6 预检四件之一,夹具须一并拷入。
|
||||
"chv-gitea-worker.path"):
|
||||
shutil.copy2(ROOT / "chv-guest" / name,
|
||||
os.path.join(guest, name))
|
||||
shutil.copy2(ROOT / "host.py", os.path.join(repo, "host.py"))
|
||||
@@ -920,6 +923,7 @@ class BuildDryRunTest(unittest.TestCase):
|
||||
# 三件进镜像,干跑输出须含拷入与 enable。
|
||||
"chv-gitea-worker",
|
||||
"chv-gitea-worker.service",
|
||||
"chv-gitea-worker.path",
|
||||
"systemctl enable chv-gitea-worker",
|
||||
# round-12 修复轮(项 4,P10):结账检测
|
||||
# 模块进镜像,干跑输出须含条件拷入行。
|
||||
|
||||
@@ -870,6 +870,93 @@ class WorkerUnitTmpfilesTest(unittest.TestCase):
|
||||
self.assertNotIn("ExecStartPre=", text)
|
||||
|
||||
|
||||
# -- (7c) worker 开机时序竞态:Condition + path 触发 ----------------------
|
||||
class WorkerUnitBootRaceTest(unittest.TestCase):
|
||||
"""round-12 修复轮第三轮(缺口 ①,P6):R12 复测 2 钉死剩下的
|
||||
226 只点名 /home/chv/workspace——工作区由 cloud-final 的
|
||||
chv-sync boot 克隆出来,晚于 multi-user.target 约 9 秒,worker
|
||||
的 5 次启动预算在工作区出现前耗尽。
|
||||
|
||||
修法选 (a)(Condition + systemd.path),不用 (b)/(c):
|
||||
- (a) 让服务根本不在工作区出现前启动:ConditionPathExists 把
|
||||
开机那次启动挡在命名空间搭建之前(干净跳过、不 226、不耗
|
||||
重启预算),.path 单元在工作区出现时再触发一次——Condition
|
||||
本身绝不重试,没有 .path 它就是一次跳过、服务永远不起。
|
||||
- (b) 放宽 StartLimit 只是把竞态窗口拉大:wake 时间在负载下
|
||||
已翻倍,预算仍可能耗尽,仍是时序赌博。
|
||||
- (c) 把工作区创建提前到 tmpfiles-setup 之前不可行:
|
||||
/home/chv 本身 07:23:40 才出现(cloud-init 建 chv 用户),
|
||||
晚于 tmpfiles-setup(07:23:24);硬约束:绝不盲目补
|
||||
tmpfiles 行。
|
||||
ExecStartPre 也救不了 226——命名空间搭建在它之前。
|
||||
"""
|
||||
|
||||
def _service_text(self):
|
||||
return (REPO_ROOT / "chv-guest"
|
||||
/ "chv-gitea-worker.service").read_text()
|
||||
|
||||
def _path_text(self):
|
||||
return (REPO_ROOT / "chv-guest"
|
||||
/ "chv-gitea-worker.path").read_text()
|
||||
|
||||
def _build_text(self):
|
||||
return (REPO_ROOT / "chv-guest" / "build.sh").read_text()
|
||||
|
||||
def test_service_gated_by_condition(self):
|
||||
self.assertIn(
|
||||
"ConditionPathExists=/home/chv/workspace",
|
||||
self._service_text())
|
||||
|
||||
def test_no_start_limit_widening(self):
|
||||
# 修法是 (a) 不是 (b):重启预算保持 systemd 默认,绝不
|
||||
# 靠放宽 StartLimit 赌工作区出现。
|
||||
text = self._service_text()
|
||||
self.assertNotIn("StartLimitBurst=", text)
|
||||
self.assertNotIn("StartLimitIntervalSec=", text)
|
||||
|
||||
def test_path_unit_triggers_on_workspace(self):
|
||||
text = self._path_text()
|
||||
self.assertIn("PathExists=/home/chv/workspace", text)
|
||||
self.assertIn("WantedBy=multi-user.target", text)
|
||||
# 不写 Unit=:默认触发同名 .service,与服务单元配对。
|
||||
self.assertNotIn("Unit=", text)
|
||||
|
||||
def test_service_still_wanted_by_multi_user(self):
|
||||
# WantedBy 保留:is-enabled 仍为 enabled;开机那次启动
|
||||
# 只会被 Condition 干净跳过,不会 226。
|
||||
self.assertIn("WantedBy=multi-user.target",
|
||||
self._service_text())
|
||||
|
||||
def test_build_enables_path_unit(self):
|
||||
text = self._build_text()
|
||||
self.assertIn("chv-gitea-worker.path:/etc/systemd/system/",
|
||||
text)
|
||||
self.assertIn(
|
||||
"systemctl enable chv-gitea-worker chv-gitea-worker.path",
|
||||
text)
|
||||
|
||||
def test_runcmd_starts_path_unit_not_service(self):
|
||||
text = self._build_text()
|
||||
self.assertIn("systemctl start chv-gitea-worker.path", text)
|
||||
# cloud-final 时工作区尚未 clone,直接 start 服务会 226。
|
||||
self.assertNotIn("systemctl start chv-gitea-worker || true",
|
||||
text)
|
||||
|
||||
def test_no_tmpfiles_line_for_workspace(self):
|
||||
# 硬约束:绝不盲目补 tmpfiles 行——/home/chv 本身晚于
|
||||
# tmpfiles-setup,补了也没用。
|
||||
self.assertNotIn("d /home/chv/workspace", self._build_text())
|
||||
|
||||
def test_buildinfo_and_lockfile_include_path_unit(self):
|
||||
self.assertIn("chv-gitea-worker.path",
|
||||
(REPO_ROOT / "chv-guest"
|
||||
/ "buildinfo.py").read_text())
|
||||
for name in ("check_lockfile.py", "generate_lockfile.py"):
|
||||
self.assertIn(
|
||||
'"chv-gitea-worker.path": "repo_commit"',
|
||||
(REPO_ROOT / "chv-guest" / name).read_text())
|
||||
|
||||
|
||||
class BuildInfoContentsTest(unittest.TestCase):
|
||||
"""round-12 修复轮(项 2,P6):worker 三件须进 BUILDINFO
|
||||
contents(无条件拷入,与 P2/P4 同款钉法)。"""
|
||||
@@ -900,7 +987,8 @@ class BuildInfoContentsTest(unittest.TestCase):
|
||||
out, "chv-guest-24.04-0.164.0-alpha.9-abc1234", "24.04",
|
||||
"0.164.0-alpha.9", "abc1234", "0" * 64, True, "1248")
|
||||
for item in ("chv-gitea-worker", "chv-sync-worker-client",
|
||||
"chv-gitea-worker.service"):
|
||||
"chv-gitea-worker.service",
|
||||
"chv-gitea-worker.path"):
|
||||
self.assertIn(item, info["contents"])
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user