feat: 长任务补齐——流式进度 + 跨进程中断 + worker 超时约定
- 长等待流式进度:run_turn 改 1 秒切片轮询(_wait_turn),每满 --progress-interval(默认 30 秒,0 关闭,CLI > 配置 > 默认)往 stderr 打 turn progress 并往链路文件记 kind=progress;进度绝不走 stdout,不破坏 dispatch JSON 与 goal 结论行/用量行解析(经 cloud 真正则验过) - 跨进程中断:interrupt 直接不生效时写 <state_dir>/interrupt-requests/<task>.json,属主等 turn 时每秒轮询到后用自己的 live 连接发 turn/interrupt 并记 interrupted;10 秒内未响应也不删文件,属主下次轮询仍处理;接线覆盖 dispatch/run/goal/goal-resume/delegate/parallel/fix-review/run-due;旧 server 不接受 interrupt_file 时退回不传再跑 - cloud worker 超时约定:--task-timeout 0 归一化为 None(不限时,与 host turn_timeout 同约定,Docker 路径仍受 docker_timeout 约束),默认 1800 不变;超时仍记 failed(现有单测合同不动) 测试:host 56 全过(新增 7)、cloud 526 全过(4 跳过,新增 1 个 0=不限时)。
This commit is contained in:
1 parent
028ef02a86
commit
1128fd3076
6 files changed
+400
-34
No files matched your search
+3
-1
@@ -683,7 +683,9 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
|
||||
`GET /metrics` 的 `by_status` 恒含七个状态键(queued / running /
|
||||
completed / failed / interrupted / merged / merge-failed),缺失
|
||||
状态为 0 而不是缺键。
|
||||
- 任务级超时:worker 的 `--task-timeout`(默认 1800 秒),与另外
|
||||
- 任务级超时:worker 的 `--task-timeout`(默认 1800 秒,0 表示
|
||||
不限时、与 host `--turn-timeout 0` 同约定,worker 级不设截止,
|
||||
但 Docker 路径仍受 `--docker-timeout` 约束),与另外
|
||||
两个参数不要混淆:`--docker-timeout` 是执行器级的单次 docker run
|
||||
超时,`--max-seconds` 是 `run_loop` 的整体墙钟上限;三者互不覆盖,
|
||||
Docker 路径下 worker 取 `min(task_timeout, docker_timeout)`
|
||||
|
||||
@@ -123,6 +123,27 @@ class WorkerTimeoutTestCase(unittest.TestCase):
|
||||
self.assertIn("worker_timeout", _audit_actions(db_path))
|
||||
tmp.cleanup()
|
||||
|
||||
def test_task_timeout_zero_means_unlimited(self):
|
||||
# 对齐 host turn_timeout:0 归一化为 None(不限时),
|
||||
# 而不是在 popen 轮询第一轮就立即超时。sleep 0.5 的
|
||||
# 任务在 task_timeout=0 下应正常完成,库里是 completed。
|
||||
tmp = tempfile.TemporaryDirectory()
|
||||
|
||||
def factory(task):
|
||||
return subprocess.Popen(
|
||||
["sleep", "0.5"], stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, text=True, start_new_session=True)
|
||||
|
||||
worker, db_path, task = _make_worker(
|
||||
tmp.name, process_factory=factory, task_timeout=0,
|
||||
max_retries=0, retry_backoff_seconds=0)
|
||||
self.assertIsNone(worker.task_timeout)
|
||||
result = worker.run_one(task)
|
||||
self.assertEqual(result["status"], "completed")
|
||||
status, _ = _task_row(db_path)
|
||||
self.assertEqual(status, "completed")
|
||||
tmp.cleanup()
|
||||
|
||||
def test_docker_timeout_converts_to_failed_and_audit(self):
|
||||
tmp = tempfile.TemporaryDirectory()
|
||||
|
||||
|
||||
+20
-4
@@ -68,6 +68,12 @@ interrupted,不用执行结果覆盖它。
|
||||
runner / 无超时的 subprocess.run)无法在中途抢占一个已阻塞
|
||||
的调用,超时只能靠执行器自身的 timeout,这是诚实边界,
|
||||
不要假装同步路径也能中途杀掉。
|
||||
- 对齐 host.py 的 turn_timeout 约定:task_timeout 传 0 时
|
||||
归一化为 None(不限时,worker 级不设截止),与 host 的
|
||||
“0 = 不限时”同约定;默认 1800 不变,不改默认就不会悄悄
|
||||
挂起自动化。worker 级不限时时,Docker 路径仍受执行器
|
||||
docker_timeout 约束(timeout=None 时 _call_runner 用
|
||||
executor 自身的 timeout),不是全链路无限。
|
||||
- max_retries(默认 1 次重试,CLI --max-retries 可调,0 表示
|
||||
不重试)是对模型任务失败的有限重试:取消不重试;可 fallback
|
||||
的失败优先走真实 fallback(见下),不再做同模型重试(fallback
|
||||
@@ -311,6 +317,14 @@ class Worker:
|
||||
self._loop_stop = False
|
||||
self.logger = get_logger("cloud.worker")
|
||||
# 任务级超时 / 失败重试(见模块 docstring 的生产级说明)。
|
||||
# 对齐 host turn_timeout:0 归一化为 None(不限时),
|
||||
# 负数同理;默认 1800 不变。归一化后 _run_attempt 里
|
||||
# task_timeout is None 的分支本来就走“无 worker 级截止”
|
||||
#(Docker 路径仍受 executor docker_timeout 约束),
|
||||
# 不用再改各执行路径;不归一化的话 0 会在 popen 轮询
|
||||
# 里第一轮就触发“已到截止时间”而立即超时,是反直觉的 bug。
|
||||
if task_timeout is not None and task_timeout <= 0:
|
||||
task_timeout = None
|
||||
self.task_timeout = task_timeout
|
||||
self.max_retries = max_retries
|
||||
self.retry_backoff_seconds = retry_backoff_seconds
|
||||
@@ -2102,10 +2116,12 @@ def main(argv=None):
|
||||
" worker 必须开,否则它会抢走本该进"
|
||||
" Docker 容器的 NULL 任务")
|
||||
parser.add_argument("--task-timeout", type=float, default=1800,
|
||||
help="每任务截止时间(秒,默认 1800);超时后真杀"
|
||||
"容器/进程组、任务置 failed;与 --docker-timeout"
|
||||
"(单次 docker run 超时)取较小值,与"
|
||||
" --max-seconds(run_loop 整体墙钟上限)不同")
|
||||
help="每任务截止时间(秒,默认 1800);0 表示不限时"
|
||||
"(与 host --turn-timeout 0 同约定,worker 级"
|
||||
"不设截止,但 Docker 路径仍受 --docker-timeout"
|
||||
"约束);超时后真杀容器/进程组、任务置 failed;"
|
||||
"与 --docker-timeout(单次 docker run 超时)取"
|
||||
"较小值,与 --max-seconds(run_loop 整体墙钟上限)不同")
|
||||
parser.add_argument("--max-retries", type=int, default=1,
|
||||
help="模型任务失败重试次数(默认 1,0 表示不重试;"
|
||||
"取消与已记 fallback 的失败不重试)")
|
||||
|
||||
Reference in new issue
Block a user