Skip to content

锁步契约

控制面与 fleet agent 之间有两类硬耦合,改动时必须按序发布:协议 schema连通性契约


一、协议 schema:extra="forbid" 的代价

packages/dn42_schemas 的所有模型继承 StrictModel,配置是 extra="forbid"frozen=True

好处是任何隐式协议漂移都在解析期直接报错,而不是被静默忽略后在别处炸开。代价是接收方必须先认识新字段,否则整份 payload 被拒。

因此发布顺序取决于数据流方向

变更数据流方向先升谁理由
DesiredState 加字段控制面 → agent先升 agentagent 解析 DesiredState,不认识新字段就整份拒收、无法 reconcile
DesiredState 删字段控制面 → agent先升控制面控制面停止下发之后,agent 才能安全地移除该字段的处理
上报 payload 加字段agent → 控制面先升控制面控制面解析上报,不认识新字段就整份拒收、对账假失败
上报 payload 删字段agent → 控制面先升 agentagent 停止发送之后,控制面才能移除

一句话记法:加字段先升接收方,删字段先升发送方。

同时改两个方向的变更需要拆成两次发布。

校验规则与哈希算法也在此列

修改 dn42_common 的校验器或 serialization 的哈希算法,同样影响两侧对同一输入的接受集与摘要口径。校验收紧可能拒掉存量配置,发布前要先全量核查。

例外:向后兼容的可选字段

上报侧的新增可选字段如果控制面用 Optional 且默认 None 接收,旧 agent 不发时控制面会 no-op——这类改动没有顺序要求。前缀级 flap 的路径证据字段就是这么加的。

判断标准是:接收方的模型能不能在字段缺席时正常构造。


二、连通性契约

改 agent 与控制面之间的连接方式(API 前缀、鉴权形态、WebSocket 路径),是另一类锁步——它不只是顺序问题,还会让自更新链路本身失效。

自更新链路的适用边界

自更新的健康门要等新版 agent 重写 agent-healthy.json,而该文件只在 WS 心跳成功时才写

于是:

  • 先升 agent:新 agent 说新协议、旧控制面不认,心跳必然失败 → 健康门超时 → 每个节点自动回滚
  • 先升控制面:旧 agent 连 wheel 都下载不到——分发端点也在新协议下。

结论:涉及连通性契约的变更,必须走 SSH 手工 wheel 路径(见 Agent 滚动升级)。

两种执行次序

两种都可行,都存在一段控制面失明窗口,数据面均不受影响——节点按最后一次已应用的配置继续转发。

agent 优先

  1. 构建 wheel 并校验产物确实携带新协议。
  2. 逐节点手工滚动。此刻起各节点陆续失联(agent 说新的、控制面还认旧的,报 404 并退避重试)。
  3. 部署新版控制面(含 nginx 配置)。三个后端服务一起换。 上线后全机群自行回连,无须再登节点。
  4. 全机群绿了之后,把 wheel 上传到控制面并设 target 版本,让自更新机制回到可用状态——否则下一次自更新会从一个不存在的旧版本起跳。

控制面优先:失明窗口从控制面上线起算,每台 agent 升完即恢复。好处是控制面这个「全有全无」的步骤可以在不碰任何节点的前提下先验证,失败只需回滚镜像。

前端与其它客户端

后端不留兼容期时,客户端仓库须同一批次发布。控制台与门户经 CORS 直连控制面;授权页与 auth-server 同源,改协议时两侧要一起换产物。


三、发布前检查清单

  • [ ] 本次变更是否动了 dn42_schemas 的模型字段?动了就按方向定顺序。
  • [ ] 是否动了 dn42_common 的校验器或哈希?动了就先全量核查存量配置会不会被新校验拒掉。
  • [ ] 是否动了 API 前缀、鉴权形态或 WS 路径?动了就不能用自更新
  • [ ] 是否有数据库迁移?加列类在代码前跑,删列类在代码后跑。
  • [ ] 三个后端服务是否一起发布?
  • [ ] 前端产物是否需要同批次发布?

golden 渲染回归(tests/unit/test_golden_rendered_hkg1.py)会挡住模板输出的意外改变——schema 默认值、模板、runtime 输出有意改变时才刷新 golden,见 贡献指南