45 lines
2.0 KiB
Markdown
45 lines
2.0 KiB
Markdown
# ADR-0002:以事件溯源作为 Run 的唯一真相
|
||
|
||
- 日期:2026-07-27
|
||
- 状态:已接受
|
||
|
||
## 背景
|
||
|
||
产品要交付的三件事——**统一时间线**、**审计**、**自动复现**——都需要回答同一个问题:
|
||
"当时到底按什么顺序发生了什么"。
|
||
|
||
同时,观测是**双通道**的:Agent 报一套,带外控制器报一套,两者时钟不同步,
|
||
主机断电时 Agent 那一路会直接消失。
|
||
|
||
## 决定
|
||
|
||
`run_events` 是 append-only 的唯一真相。`runs` / `run_steps` / `recovery_actions`
|
||
是可以从事件重放重建的**读模型**。
|
||
|
||
序号 `seq` 由控制平面单点分配(不是时间戳排序),Agent 事件与带外事件进同一条序列。
|
||
|
||
仓储层**不提供** `update_run_state()` 类 API。改状态的唯一入口是
|
||
`events/recorder.py::record()`,它写事件并同步更新投影。
|
||
|
||
## 理由
|
||
|
||
- 主机断电时,Agent 的最后几条日志可能永远到不了。带外控制器的观测
|
||
(掉电时刻、画面指纹)是那段时间唯一的证据。两路事件必须在同一条时间线上
|
||
才能交叉校验——"Agent 最后一条日志 seq=812,带外报掉电 seq=813"这种判断
|
||
只有单点序号能给。
|
||
- 审计要求记录"操作者、审批者、命令模板版本、输入参数、结果、撤销动作"
|
||
(方案 §6.6)。这些天然是事件,硬塞进当前状态表会做成一堆冗余字段。
|
||
- 自动复现(§8.4)需要"失败前后的最小步骤区间"。有完整事件流才能裁剪。
|
||
|
||
## 代价
|
||
|
||
- 写路径变长,每次状态变化多一次插入。
|
||
- `run_events` 会很大:100 循环 × 每轮 ~30 事件 = 3000 条/Run。
|
||
缓解:事件按 run_id 分区友好,日志正文不进事件表(只存对象存储 URI)。
|
||
- 开发者容易忍不住直接 UPDATE。缓解:仓储层不给这个 API,code review 卡这一条。
|
||
|
||
## 什么时候推翻
|
||
|
||
不推翻。真要优化,是加快照(每 N 个事件存一次状态快照加速重放),
|
||
不是放弃事件溯源。
|