From 42b295fc719232ee99e590526649a1f293b0755e Mon Sep 17 00:00:00 2001 From: host Date: Sat, 7 Nov 2026 12:18:19 +0000 Subject: [PATCH] =?UTF-8?q?fix(chv):=20=E9=98=B6=E6=AE=B5=202=20syncd=20?= =?UTF-8?q?=E6=94=B9=E8=B5=B0=20systemd=20RuntimeDirectory=3D/run/chv-sync?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tmpfiles 行 d /run/chv/syncd 0755 chv chv 没用:tmpfiles-setup 跑在 cloud-init 建 chv 用户之前,属主解析失败整行被跳过, 客内 /run/chv/syncd 根本不存在(2026-11-07 真机实测:镜像 里有这行、目录没有,syncd 反复退出、restart counter 57)。 改走单元 RuntimeDirectory=chv-syncd(服务启动时由 systemd 按服务用户建目录,不依赖启动时序);状态写失败改为记日志 不杀进程;daemon 与驱动的 syncd 路径同步改到 /run/chv-syncd/。 --- chv-guest/build.sh | 4 ++-- chv-guest/chv-daemon.py | 12 +++++++----- chv-guest/chv-syncd.py | 21 +++++++++++++++++---- cloud/tests/test_chv_opt_stage2.py | 19 ++++++++++++------- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/chv-guest/build.sh b/chv-guest/build.sh index 86dce9f..f1d3365 100755 --- a/chv-guest/build.sh +++ b/chv-guest/build.sh @@ -318,8 +318,8 @@ VC_ARGS=(-a "$OUT/disk.raw" --run-command 'printf "[Unit]\nDescription=CHV browser sub-agent (Muse P2)\nAfter=network.target\n\n[Service]\nType=simple\nUser=chv-browser\nGroup=chv-browser\nExecStart=/usr/bin/python3 /usr/local/bin/chv-browser\nRestart=on-failure\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-browser.service' --run-command 'printf "[Unit]\nDescription=CHV guest runtime daemon (stage 2: heartbeat/resources/snapshot)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 /usr/local/bin/chv-daemon\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-daemon.service' --run-command 'printf "[Unit]\nDescription=CHV guest exec daemon (stage 2: shell/files/codex hosting)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 /usr/local/bin/chv-execd\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-execd.service' - --run-command 'printf "[Unit]\nDescription=CHV Gitea sync daemon (stage 2: resident pull/push loop)\nAfter=network.target\n\n[Service]\nType=simple\nUser=chv\nGroup=chv\nExecStart=/usr/bin/python3 /usr/local/bin/chv-syncd\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-syncd.service' - --run-command 'printf "d /run/chv 0755 root root -\nd /run/chv/shots 0755 chv-browser chv-browser -\nd /run/chv/tasks 0755 root root -\nd /run/chv/syncd 0755 chv chv -\n" > /etc/tmpfiles.d/chv.conf && systemctl enable chv-broker chv-browser chv-daemon chv-execd chv-syncd || true') + --run-command 'printf "[Unit]\nDescription=CHV Gitea sync daemon (stage 2: resident pull/push loop)\nAfter=network.target\n\n[Service]\nType=simple\nUser=chv\nGroup=chv\nRuntimeDirectory=chv-syncd\nRuntimeDirectoryMode=0755\nExecStart=/usr/bin/python3 /usr/local/bin/chv-syncd\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-syncd.service' + --run-command 'printf "d /run/chv 0755 root root -\nd /run/chv/shots 0755 chv-browser chv-browser -\nd /run/chv/tasks 0755 root root -\n" > /etc/tmpfiles.d/chv.conf && systemctl enable chv-broker chv-browser chv-daemon chv-execd chv-syncd || true') # P1 交接(Muse P1 的 vsock_notify.py):文件在仓库里存在时拷入 # 镜像(P1 分支才有,本分支尚无);不存在时只警告、不失败—— # 合并后自动生效,P2 不改该脚本内容。 diff --git a/chv-guest/chv-daemon.py b/chv-guest/chv-daemon.py index 96b216a..c0bb4fe 100644 --- a/chv-guest/chv-daemon.py +++ b/chv-guest/chv-daemon.py @@ -41,11 +41,13 @@ DAEMON_SOCK = "/run/chv/daemon.sock" SNAPSHOT_READY_PATH = "/run/chv/snapshot-ready.json" EXECD_SOCK = "/run/chv/execd.sock" EXECD_TASKS = "/run/chv/execd-tasks.json" -# syncd 的状态文件在它自己的 chv 所有子目录里(见 build.sh -# tmpfiles:/run/chv 本身 0755 root:root,chv 身份的 syncd -# 写不进——2026-11-07 真机实测:状态文件写失败致 syncd -# 反复退出、systemctl 长期 activating)。 -SYNC_STATUS = "/run/chv/syncd/sync-status.json" +# syncd 的状态文件在 systemd RuntimeDirectory 里(见 build.sh +# 的 syncd 单元:RuntimeDirectory=chv-syncd,服务启动时由 +# systemd 按 chv 属主建 /run/chv-syncd;不要放回 /run/chv/ +# 下——tmpfiles-setup 跑在 cloud-init 建 chv 用户之前,带 chv +# 属主的 tmpfiles 行会被跳过,目录根本不存在,2026-11-07 +# 真机实测 syncd 因此反复退出、长期 activating)。 +SYNC_STATUS = "/run/chv-syncd/sync-status.json" WORKSPACE = "/home/chv/workspace" HEARTBEAT_INTERVAL = 10.0 MAX_REQUEST_BYTES = 65536 diff --git a/chv-guest/chv-syncd.py b/chv-guest/chv-syncd.py index 0e2165b..7edccee 100644 --- a/chv-guest/chv-syncd.py +++ b/chv-guest/chv-syncd.py @@ -30,8 +30,14 @@ import sys import threading import time -SYNC_SOCK = "/run/chv/syncd/syncd.sock" -STATUS_PATH = "/run/chv/syncd/sync-status.json" +# systemd RuntimeDirectory(见 build.sh 的 syncd 单元):服务启动 +# 时由 systemd 按服务用户(chv)建 /run/chv-syncd,启动时序 +# 不依赖 cloud-init 先建好 chv 用户。不要放回 /run/chv/ 下—— +# tmpfiles-setup 跑在 cloud-init 建 chv 用户之前,带 chv 属主 +# 的 tmpfiles 行会被跳过(2026-11-07 真机实测:目录根本没建, +# syncd 反复退出、长期 activating)。 +SYNC_SOCK = "/run/chv-syncd/syncd.sock" +STATUS_PATH = "/run/chv-syncd/sync-status.json" WORKSPACE = "/home/chv/workspace" SYNC_BIN = "/usr/local/bin/chv-sync" DEFAULT_INTERVAL = 300.0 @@ -116,8 +122,15 @@ class Syncd: "consecutive_errors": 0, "cycles": 0} def _write_status(self): - _atomic_write(self.status_path, - (json.dumps(self._status) + "\n").encode()) + try: + _atomic_write(self.status_path, + (json.dumps(self._status) + "\n").encode()) + except OSError as exc: + # 状态文件是可观测性,不是同步本身:写失败记一 + # 行日志、不杀进程(2026-11-07 真机教训:写失败 + # 抛到主线程致 syncd 反复退出、长期 activating)。 + print("chv-syncd: status write failed: %s" % exc, + file=sys.stderr, flush=True) def run_cycle(self): """一轮同步:先 pull 再 push;任一步失败即停住记 error。""" diff --git a/cloud/tests/test_chv_opt_stage2.py b/cloud/tests/test_chv_opt_stage2.py index 43f8cc4..627c958 100644 --- a/cloud/tests/test_chv_opt_stage2.py +++ b/cloud/tests/test_chv_opt_stage2.py @@ -290,19 +290,24 @@ class SyncdTest(unittest.TestCase): return path def test_syncd_paths_under_chv_owned_subdir(self): - # 2026-11-07 真机教训:/run/chv 是 0755 root:root, - # chv 身份的 syncd 写不进状态文件与 socket,反复退出、 - # systemctl 长期 activating。socket 与状态文件须在 - # chv 所有的 /run/chv/syncd/ 子目录里。 + # 2026-11-07 真机教训两则:① /run/chv 是 0755 + # root:root,chv 身份的 syncd 写不进;② tmpfiles + # 行 d /run/chv/syncd 0755 chv chv 也没用—— + # tmpfiles-setup 跑在 cloud-init 建 chv 用户之前, + # 属主解析失败整行被跳过、目录根本没建,syncd 反复 + # 退出、长期 activating。正解:systemd + # RuntimeDirectory(服务启动时由 systemd 按服务用户 + # 建 /run/chv-syncd),状态写失败也不杀进程。 self.assertTrue(self.mod.SYNC_SOCK.startswith( - "/run/chv/syncd/")) + "/run/chv-syncd/")) self.assertTrue(self.mod.STATUS_PATH.startswith( - "/run/chv/syncd/")) + "/run/chv-syncd/")) daemon = _load_guest_script("chv-daemon.py") self.assertEqual(daemon.SYNC_STATUS, self.mod.STATUS_PATH) with open(_repo("chv-guest", "build.sh")) as fh: text = fh.read() - self.assertIn("d /run/chv/syncd 0755 chv chv -", text) + self.assertIn("RuntimeDirectory=chv-syncd", text) + self.assertNotIn("d /run/chv/syncd ", text) def test_resolve_repo_zero_one_many(self): repo, reason = self.mod.resolve_repo(self.mod.WORKSPACE)