Files
orbit/docs/reports/phase-6-fault-boundaries.md

75 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# P6 首批:派发未送达与事务提交故障边界
日期:2026-09-18。本批补A04、W01/W13的精确故障切点,并补W16最终回执、旧查询结果与下一轮唤醒的竞争。工具只编译进测试二进制,生产服务、SDK运行代码、接口、模型和迁移不增加故障开关;数据库继续禁止外键。
## A04:派发已提交,Worker尚未收到
[公共SDK进程测试](../../sdk/go/dispatch_process_integration_test.go)使用[真实WS故障网关](../../sdk/go/worker_integration_test.go)。网关已从真实Orbit收到`task.assign`帧,但在转交业务Worker之前截留它,并阻止重连。
测试通过独立HTTP连接确认同一任务已处于LEASED、存在一次持久尝试、authorized_at为空。Worker实际处理器调用数仍为0。随后强杀原Orbit并Wait确认退出;该帧始终没有交给Worker,网关丢弃它。另起真实Orbit节点并按原提交键核对回执。
保持Worker离线,等待原30秒租约自然到期,不修改数据库时钟。新节点将未授权尝试记录为EXPIRED,ended_at不早于原lease_until,原任务回到WAITING且没有未知副作用标记。恢复WS后完成原任务:
- 业务处理器恰好执行1次;总尝试恰好2次,第一次未获授权,第二次SUCCEEDED。
- 沿用原任务、提交回执、总截止和2次尝试预算;RECONCILE_ONLY策略保持。
- 并发上限为1的队列可重新派发,证明旧预占已释放;完成后的未终结任务额度为0。
这条证据针对“未送达业务Worker”。帧已到测试网关,不声称平台从未向网络写过该帧,也不将已送达但未开始的场景混为同一切点。SDK不获得网关保存的旧执行秘密。
## W01/W13:MySQL COMMIT前后真实断线
[MySQL包代理及八个场景](../../internal/storage/mysql/commit_fault_integration_test.go)只允许连接一次性、明文、loopback测试数据库,不修改生产TLS配置,不输出SQL、凭据或业务正文。按MySQL包头逐包转发,识别真实COM_QUERY COMMIT:
| 切点 | 代理实际动作 | 独立数据库连接应观察到的事实 |
| --- | --- | --- |
| BEFORE | 不把COMMIT转交MySQL,关闭连接 | 尚未提交的状态、任务、身份和消费确认全部回滚 |
| AFTER | 收到真实MySQL成功包,核验IN_TRANS已清除;不把确认交给驱动,关闭连接 | 数据已经完整提交,调用方收到连接错误,不能当作回滚后换身份 |
四种事务各覆盖两个切点:启动实例及首批任务、结果消费及创建后继、PENDING结果及建立下一轮等待、下一轮任务及轮次预算递增。故障调用必须返回错误,同时代理必须证明到达所选切点;不能仅因请求报错就推断发生过COMMIT。
通过不经过故障代理的连接读取真实表,检查实例/任务数量、Inbox处理确认、查询等待或轮次原子性。随后使用新运行器处理原候选,或用原启动键重报。最终每个场景恰好两个任务和两次尝试,无待处理Inbox或许可遗留,原版本、原流程/查询截止及尝试预算保持。PENDING等待仍从原结果事件时间计算,不从重报时间重新开始。
这证明数据库连接在提交边界断开的恢复行为,不等于MySQL进程宕机、复制切换、丢失已确认事务或磁盘故障验收。它也不将HTTP回执丢失冒充数据库提交结果不明。
## W16:最终回执、旧查询结果、下一轮同时竞争
[六种入队顺序](../../internal/storage/mysql/workflow_final_signal_race_integration_test.go)使用真实MySQL锁等待确认三个事务都已重叠,再等原下一轮时间自然到达后放行。参与者分别是最终DONE信号受理、上一轮PENDING结果的重复消费、下一轮时钟。
上一轮必须已经保存等待,下一轮才可能到期。因此此处结果参与者是旧结果重放,测试明确保留这个因果关系,不构造不可能同时存在的“未提交PENDING和已到期下一轮”。已授权查询与新结果、冲突/未知结果的处理另由[现有信号运行回归](../../internal/storage/mysql/workflow_signal_runtime_integration_test.go)及真实进程恢复覆盖。
观察到两类合法顺序:最终信号先受理时,不创建第二轮;下一轮先创建时,最终信号取消尚未授权的第二轮查询并收集其结果。两者都只执行第一轮查询和一个后继任务,只完成一个等待。再次投递旧PENDING和旧时钟不能重新打开轮次或多建后继;原信号回执、版本、截止和精确输出保持。
## 复现和验证
```sh
make fmt test vet build
make integration
```
标准脚本的存储组自动执行`TestIntegrationWorkflowCommitFaults`与`TestIntegrationWorkflowFinalSignalRace`;原服务退出后的Worker进程组自动执行`TestIntegrationWorkerProcessUndeliveredAssignment`。无需额外生产依赖或手工开启故障环境变量。
定向环境:macOS arm64、Go 1.27.1、Docker MySQL 8.4.10、独立临时数据库及真实Orbit进程。最终定向存储组14个子场景退出0(21.100秒),A04进程组退出0(34.080秒)。MySQL驱动的预期EOF来自代理确实切断COMMIT连接,不是测试失败。首次编译纠正了测试误用的队列资源方法,改用已公开的GetQueue;未因此增加接口或扩大发放权限。
基础`make fmt test vet build`退出0。最终标准全量回归退出0:23个可运行SDK示例、2个独立业务模块,以及完整存储、协议与真实进程恢复链路均通过。标准脚本使用竞态检测;本批测试源码自启动完整回归后未再修改。
| 标准回归分组 | 结果与测试耗时 |
| --- | --- |
| 存储及迁移全部场景 | 退出0,266.966秒;包含八个COMMIT切点及六个W16入队顺序 |
| 原流程运行器进程全停/接管 | 退出0,49.022秒 |
| 独立业务Inbox | 退出0,35.638秒 |
| 独立业务Outbox | 退出0,34.167秒 |
| Worker/结果订阅进程故障 | 退出0,128.097秒;包含A04自然租约到期场景32.72秒 |
| 公共SDK流程暂停全停/恢复 | 退出0,13.845秒 |
以上耗时仅用于复现核对,不是吞吐或延迟指标。无外键升级回归同时确认:历史70个外键全部移除,323个原索引组成部分及任务数据保留,当前外键为0,重新引入外键的结构漂移被拒绝。模型和中文注释要求没有改变。
本批证据与[前一批竞争/全停回归](phase-4-5-races.md)共同补齐P4/P5功能阶段核对所列的具体缺口;[P4/P5报告](phase-4-5.md)据原功能清单记录阶段验收通过。P6进行中,P7及完整首版交付尚未完成。
最终文档检查:1184个本地链接、68个JSON块,错误0;`git diff --check`通过。
## 保留的生产门槛
本批关闭上述具体切点缺口,不代表故障模型或负载已穷尽。完整首版仍需运行指标/告警、可轮换凭据及安全重绑、历史保留/归档、慢消费者和重连风暴、六类容量负载、MySQL失联/主节点切换、备份恢复、跨故障域部署、SDK发布兼容和完整接入示例。
阶段范围和下一步见[持续计划](../08-development-plan.md)、[P4/P5核对](phase-4-5.md)与[原生产验收门槛](../05-production-acceptance.md)。当前单机数据库证据不能换算成生产HA、RPO/RTO或百万企业容量承诺。