Skip to content

服务侧部署忙锁:上线前自动部署可打断在飞转写任务(deferred gate finding reliability-deploy-during-active-task) #249

Closed

Description

@zj1123581321

背景

2026-10-15 21:38 手动部署替换容器,打断了 21:33 提交的转写任务(task_ed2316…,被启动孤儿恢复标 failed,错误信息 Task interrupted by service restart)。任务队列在内存中,进程替换即丢失。

已落地(PR #246)

  • D3 自动部署接入(push main 自动部署 + workflow_dispatch 手动补发)
  • 部署侧忙锁参数已启用:busy_lock_file=/opt/media/VideoTranscriptAPI/.deploy-state/busy.lock,timeout 600s
  • 服务侧 BusyLock + compose 挂载(本 PR 第四轮)
  • 事前 pre-flight + 事后 verify-busy-lock-inode 双闸(本 PR 第六轮 / gate primary 第 4 轮):替换前对当前运行容器做 inode 对账,失败则 ship 不跑

修复计划(本 issue 的收口判据)

服务侧忙锁(卡 VideoTranscriptAPI-20261015-02)已在 PR #246 落地:

  • BusyLock 模块:转写任务受理→终态全程持 LOCK_SH(非阻塞 admission,拿不到返回 503)
  • 终态路径(成功/失败收口)释放
  • compose 部署种子加 ./.deploy-state:/app/.deploy-state 挂载
  • 跨进程真实 flock 测试(部署侧 LOCK_EX 持有时 admission 必须拒绝;终态后必须放行)

服务侧合并且第二次及以后的 D3 自动部署:有在飞任务时 defer(exit 3,旧容器保留);无挂载/inode 不一致时 pre-flight 失败、ship 不执行。本 issue 在首次人工容器重建完成、pre-flight 已在一次自动部署中通过后可关。

首次容器重建(不走 deploy.yml)

旧容器没有 .deploy-state 挂载,pre-flight 对它会失败——这是设计(fail-closed),不是要绕过的闸。

合并后由主脑 ssh 在部署目录执行 docker compose up -d 做第一次容器重建(把 compose 换成带挂载的种子),不要走 deploy.yml / workflow_dispatch。步骤:

  1. 确认非终态任务为 0:sqlite3 data/cache/cache.db 'select count(*) from task_status where status not in ("success","failed")' 为 0
  2. 确认部署目录 compose 已是本仓种子形态(含 ./.deploy-state:/app/.deploy-state)
  3. docker compose up -d
  4. 确认宿主 .deploy-state/busy.lock 存在(没有则 touch),且容器内 /app/.deploy-state/busy.lock 与宿主 stat -c %i 一致

此后自动路径的 pre-flight 才会通过。

过渡期缓解(首次重建完成前)

  • 不要用 push main / workflow_dispatch 触发 deploy.yml(pre-flight 会对旧容器失败,ship 不会跑)
  • 部署前人工确认无非终态任务
  • 尽量避开业务高峰

Refs: PR #246, gate run 38770699923

Activity

  1. zj1123581321 commented on Oct 15, 2026

    @zj1123581321
    CollaboratorAuthor

    顾问裁决补充(consult 20261016-012652-codex):服务侧忙锁改为并入 PR #246 同分支续做(原独立卡取消)。新增一条过渡期不变式:PR 合并后的首次自动部署发生在旧容器(无持锁代码)运行时,忙锁保护生效前存在空档——首次部署前必须人工确认无非终态任务为 0;之后的部署由忙锁自动 defer。

  2. zj1123581321 commented on Oct 15, 2026

    @zj1123581321
    CollaboratorAuthor

    gate primary 第 4 轮语义更新(PR #246 第六轮):

    • 自动路径现在是 pre-flight(替换前对当前容器 inode 对账)→ ship → verify(替换后对新容器对账)。旧容器无挂载时 pre-flight 失败、ship 不跑(fail-closed)。
    • 首次容器重建不走 deploy.yml:主脑 ssh 执行 docker compose up -d(先确认非终态任务为 0)。pre-flight 从第二次自动部署起自然通过。
  3. zj1123581321 commented on Oct 15, 2026

    @zj1123581321
    CollaboratorAuthor

    第 6 轮(gate 第 5 轮 findings 修复)语义变化:服务侧 BusyLock 从 fail-open 改为 fail-closed——锁基础设施异常(挂载/权限/flock 故障)时拒绝新任务(503),不再放行。影响窗口:PR #246 合并后到首次人工部署之间,新任务会被拒(窗口分钟级,主脑控制);首次部署完成后窗口消失。已接任务不受影响(锁已持有,跑完到终态正常释放)。

  4. zj1123581321 commented on Oct 15, 2026

    @zj1123581321
    CollaboratorAuthor

    首次人工部署操作步骤更新(服务侧禁自建锁文件契约):docker compose up -d 前必须先预建锁文件:mkdir -p /opt/media/VideoTranscriptAPI/.deploy-state && touch /opt/media/VideoTranscriptAPI/.deploy-state/busy.lock。不预建的后果:服务侧 ENOENT → 全部新任务 503(fail-closed 按设计工作,但服务不可用)。首次部署 verify 通过后会自动写入 .deploy-state/last_verified_good(回滚锚点);若首次部署 verify 即失败报 rollback_failed 属预期 fail-loud,按 DEPLOY.md 灾备兜底。

  5. zj1123581321 commented on Oct 16, 2026

    @zj1123581321
    CollaboratorAuthor

    进度:PR #246 已带闸合并(merge commit 6080131,主脑获用户授权)。首次人工部署完成:.deploy-state/busy.lock 预建、容器带挂载重建、inode 对账一致(host=container=1840832)、/livez ok、公网入口 200。剩余:等待 deploy workflow 排队(fleet 同 host 部署槽串行)自动部署含 BusyLock 的新代码——届时 pre-flight/verify 全链首次实弹运行,本 issue 的忙锁闭环以该次部署成功为收官判据。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions