fix: worker 代码变化自退 + 部署时清孤儿 worker

2026-10-27 暂存清理实弹:05:51 的旧 docker worker 成孤儿后
继续用旧代码抢任务、按老路失败且不删暂存;autoscaler 用
start_new_session=True 拉 worker、部署重启杀不到非 unit 进程。

两层修:
1. worker 常驻循环每轮边界比对 worker.py 文件签名
   (mtime_ns + size),部署换目录后必变,变了就以 75
   (EX_TEMPFAIL,失败类)退出——检查点在轮次边界,绝不
   中断在跑任务。退出后:unit 托管的由 systemd on-failure
   重拉新版,autoscaler 托管的由 reconcile 重拉,孤儿直接
   消失。附带好处:以后部署不用再手动重启本地 worker。
2. host_deploy.sh 换目录后、启动服务前清孤儿:按 cgroup
   过滤杀非 .service 的 cloud.worker 进程(桥接修补前就存
   在的孤儿),先 TERM(跑完当前任务再退),30 秒未退的
   再 KILL;unit 托管的(如 host-1)不动。pgrep 用
   [c]loud.worker 写法,避免检查命令自匹配。

测试:test_worker.py 新增 2 个(签名变了则 75 退出且不认领、
签名不变则跑到 max_seconds);反向验证:撤掉 worker 修复
时正好前者变红。清扫过滤器主机干跑:两个现役 worker 均
为 unit cgroup,全 KEEP,杀 0 个。全量 deploy 19、host
62、cloud 619 全过。
This commit is contained in:
host committed 2026-10-27 11:23:49 +08:00
1 parent c007fafcc7
commit 57f8852634
3 files changed
+86

No files matched your search

+33
View File
@@ -160,6 +160,16 @@ DEFAULT_MODEL = "gpt-5.6-terra"
_ALREADY_EXISTS_MARKER = "已存在"
def _code_signature():
"""本文件签名(mtime_ns, size):部署 staging 换目录后必变;
取不到时返回 None(此时自退检查跳过,绝不误杀)。"""
try:
st = os.stat(os.path.abspath(__file__))
return (st.st_mtime_ns, st.st_size)
except OSError:
return None
def _missing_key_error(env_key):
"""缺 key 的标准错误:只含 env_key 名,与 relay 侧的报错一致。"""
return "Missing environment variable: %s" % env_key
@@ -403,6 +413,16 @@ class Worker:
self.capacity = int(capacity)
except (TypeError, ValueError):
self.capacity = 1
# 常驻启动时的代码签名:run_loop 每轮边界比对,变了就自退
# (见 run_loop 的代码变化自退检查)。
self._code_signature = _code_signature()
def _code_changed(self):
"""启动后 worker.py 是否被换过(部署换目录/手动改代码):
签名取不到时一律当没变,不误杀。"""
now = _code_signature()
return (now is not None and self._code_signature is not None
and now != self._code_signature)
def register(self):
"""向控制面注册本 worker(写 workers 表,失败只记日志)。
@@ -2651,6 +2671,19 @@ class Worker:
if max_seconds is not None and \
time.time() - started >= max_seconds:
break
# 代码变化自退:部署换目录后,旧代码 worker 若还抢
# 任务会污染生产(2026-10-27 实弹:05:51 的旧
# docker worker 成孤儿后继续用旧代码抢任务、按老路
# 失败且不删暂存)。检查点在轮次边界,绝不中断在跑
# 任务;退出码 75(EX_TEMPFAIL,失败类):unit 托管
# 的由 systemd on-failure 重拉新版,autoscaler 托管
# 的由 reconcile 重拉,孤儿直接消失。单次 run_all
# 不走这里(进程本来就退出)。
if self._code_changed():
log_event(self.logger, "worker_code_changed_exit",
message="worker code changed, exiting "
"for restart")
raise SystemExit(75)
try:
results = self.run_all()
except Exception as exc: