Writer SIGKILL 恢复门禁诊断(2026-08-23)¶
问题与根因¶
DurableMessageCommitter 的状态机有 reserve、record/history、Outbox durable publish、
mark COMMITTED 四段持久边界,但此前没有能在真实 writer 进程精确停在中间边界的
测试工具。内存 fake 或 panic!/abort() 不能证明操作系统杀进程后,Redis 与
Redpanda 的真实恢复路径仍按首次 intent 收敛。
变更范围¶
crates/qim-writer/src/committer.rs:仅在fault-injectionfeature 下,在三个 已持久化窗口调用测试钩子。crates/qim-writer/src/fault.rs:解析显式测试环境变量、同步写 marker、永久等待 外部SIGKILL;默认生产构建不包含该模块。scripts/integration/writer-kill9.sh:使用隔离 Redis + Redpanda、真实五服务和 外部kill -9运行 M-COMMIT-02/03/04。
不会改变状态机顺序、生产配置、共享 Compose/acceptance 脚本或存储契约。
三个精确窗口¶
| 门禁 | marker point |
证实的持久事实 | 禁止已经发生的操作 |
|---|---|---|---|
| M-COMMIT-02 | after-reserve |
reserve、首次坐标已原子持久化 | record/history、Outbox |
| M-COMMIT-03 | after-record |
record 与 history 已原子持久化 | Outbox publish |
| M-COMMIT-04 | after-outbox-publish |
Redpanda 已返回 durable delivery | mark_outbox_published / COMMITTED |
marker 含 PID、稳定幂等键及首次 message_id/conversation_seq,但不含 token 或正文。
脚本在读取完整 marker 后执行 kill -9 "$pid";钩子本身从不 panic、abort 或自行退出。
验收条件¶
每个窗口使用不同 sender/receiver/cmid:
- marker 的 PID 必须是脚本实际杀掉的 writer 进程,且
wait返回信号退出码。 - 未注入故障的新 writer 重启后,以相同 payload + cmid 重试,ACK 的
message_id/conversation_seq必须等于 marker 首次坐标。 - 相同 cmid、不同 payload 必须收到 ERROR,不得返回 ACK。
PULL_HISTORY中该message_id恰一条;发送者与接收者PULL_MAILBOX各恰一条。- 从 Outbox topic 读取时,该
message_id的逻辑键集合大小必须为一;M-COMMIT-04 可有同 key 的物理重发,后续 fanout/mailbox 必须按稳定 ID 收敛。
运行方式与外部条件¶
scripts/integration/writer-kill9.sh
需要 Docker daemon、隔离 Redis + Redpanda 镜像、Rust release 构建所需依赖,以及
本机可对脚本启动子进程发送 SIGKILL。任一依赖不可用必须失败并保留工件,不能标绿。
已执行验证(2026-08-23)¶
执行命令:
scripts/integration/writer-kill9.sh
隔离工件目录:/tmp/qim-artifacts.qim-it-u1000-1787495807-735631.wmI8gL。
| 门禁 | SIGKILL 退出码 | 恢复验证 | Outbox 物理记录 |
|---|---|---|---|
M-COMMIT-02 / after-reserve |
137 | ACK 首次坐标、历史一条、两侧邮箱各一条 | 1 |
M-COMMIT-03 / after-record |
137 | ACK 首次坐标、历史一条、两侧邮箱各一条 | 1 |
M-COMMIT-04 / after-outbox-publish |
137 | ACK 首次坐标、历史一条、两侧邮箱各一条 | 2(同一 canonical envelope) |
三项均验证了相同 cmid、不同 payload 会收到 ERROR。M-COMMIT-04 的两条物理
Outbox 记录是杀进程发生在 publish 后、写 publication 前的预期重发;脚本逐条解码并
确认同一 message_id 的 canonical envelope 集合大小为一。