Files
orbit/docs/reports/phase-3-result-inbox.md

84 lines
6.8 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.
# P3 业务 Inbox 与消费者故障回归
验证日期:2026-09-17。交付 [独立 MySQL Inbox 示例](../examples/result-inbox.md)。可靠订阅已具备生产者事务日志、HTTP/WSS、管理及消费 SDK、事务接收示例与对应故障证据。P3仍需补齐原功能范围中的批量受理、独立任务核查/人工重试、查询筛选和业务Outbox接入,持续目标保持进行中。
## 1. 交付与模块边界
- [独立示例模块](../../examples/result-inbox/go.mod) 引用正式SDK和MySQL驱动,不依赖平台内部模型/存储,不给核心SDK增加数据库依赖。GOWORK=off可单独构建、测试和运行。
- [业务存储](../../examples/result-inbox/internal/inbox/store.go) 以固定消费者用途、队列、事件身份去重;冻结文档摘要冲突拒绝确认。Inbox与任务结果视图同事务提交,视图仅接受更高任务版本。
- [接收方模型](../../examples/result-inbox/internal/inbox/model.go) 与 [独立SQL](../../examples/result-inbox/internal/inbox/schema.sql) 包含2个实体、17个字段,Go与SQL均有中文注释,无外键和级联删除。平台本身仍是35个实体、289个直接声明持久化字段,未增加平台迁移。
- [可运行CLI](../../examples/result-inbox/cmd/receiver/main.go) 提供init、demo、consume;初始化不覆盖已有表,持续消费核对订阅队列,演示核对业务重复、ACK及订阅暂停。
- Makefile加入独立示例的fmt/race/vet/build,统一集成脚本单独创建orbit_receiver_test数据库,与平台orbit_test隔离。
## 2. 验证环境与结果
macOS arm64、Go1.27.1、Docker MySQL8.4.10、一次性数据库、回环HTTP/WSS、真实Orbit和消费者子进程。数据库代理只用于本地无TLS的COMMIT故障注入,测试不会连接既有业务库;脚本结束清理自己的容器和临时凭据。
```sh
make fmt test vet build
make integration
cd examples/result-inbox
GOWORK=off go test -race ./...
GOWORK=off go vet ./...
```
| 分组 | 本次结果 |
| --- | --- |
| 全量平台MySQL集成 | PASS,91.331秒 |
| SDK Worker连接/自动运行器 | PASS,87.413秒 |
| SDK计划 / 队列 / 订阅管理 / 应用额度 | PASS,5.637 / 4.449 / 33.392 / 1.395秒 |
| SDK事件连接与自动消费 | PASS,50.959秒 |
| 新业务接收方六项集成 | PASS,35.512秒 |
| 其中真实COMMIT响应丢失 | PASS,0.02秒 |
| 其中消费者提交后/ACK前SIGKILL | PASS,34.20秒;使用真实30秒交付租约 |
| 原九项真实进程故障 | PASS,60.953秒;加上新消费者故障共十项 |
| 完整接入示例 | 15个全部通过,包括新的独立Inbox demo |
| 根及独立示例fmt/race/vet/build | PASS |
第一次专项启动脚本使用了不存在的测试名,导致未建平台表而失败;修正为实际的TestIntegrationSchemaAndConstraints后,专项及上表全量回归通过。没有把“无测试运行”的输出当成验证证据。
实际demo输出:
```text
receiver_schema_initialized=true foreign_keys=0
task=<本次任务ID> state=CANCELLED inbox_duplicate=true applied_events=1
ack_confirmed=1 pending=0 subscription_enabled=false
```
## 3. 正确性与数据证据
[真实存储测试](../../examples/result-inbox/internal/inbox/store_integration_test.go) 以8个并发事务处理同一事件:1次首次处理、7次已提交重复,Inbox只有1行,业务applied_events=1。相同事件ID改动内容返回冲突,原结果及推进次数保持不变。
迟到的低版本事件保存Inbox历史但不回退视图;同任务同版本不同事件ID整体回滚。不同消费者用途有独立去重范围,错队列被拒绝。测试用MySQL触发器使业务更新实际失败,验证Inbox及占位视图一起回滚,移除注入后原身份可重新提交。
[文档身份测试](../../examples/result-inbox/internal/inbox/store_test.go) 验证对象键顺序、空白和同一时刻的时区表达不改变摘要;业务数字变化会改变摘要。保留9007199254740993和HTML字符,拒绝重复JSON键、无效JSON/UTF-8和未配对UTF-16转义。
真实information_schema断言接收方2张表、17个中文列注释、0个外键。平台原v7升级回归继续确认70个历史外键最终为0、325项原索引列定义保留。
## 4. MySQL COMMIT响应丢失
[代理测试](../../examples/result-inbox/internal/inbox/commit_loss_integration_test.go) 转发真实MySQL握手、事务和语句。收到COMMIT对应的成功OK包后,代理丢弃该包并断开连接;没有伪造成功响应,也没有直接修改表制造已提交状态。
Apply返回ErrCommitUnconfirmed而非成功。测试用另一条直连查询确认Inbox及业务视图确实已经提交,再用新建Store处理同一冻结事件:命中相同摘要,返回Duplicate=true,业务推进次数保持1。测试日志中的MySQL unexpected EOF是这次有意断连的结果。
这证明本地事务提交不明时按稳定事件身份核对的路径,不是MySQL主从切换、磁盘故障或跨地域复制验收。
## 5. 消费者真实进程强杀
[消费者子进程测试](../../examples/result-inbox/internal/inbox/consumer_process_integration_test.go) 使用正式EventConsumer与Store:
1. 首个进程在Apply成功后停在测试屏障,尚未从处理器返回nil,因此SDK未发送ACK。
2. 父进程查询业务数据库,确认Inbox与视图均已提交、applied_events=1;查询平台,确认交付仍LEASED且acknowledged_at为空。
3. SIGKILL消费者,用Wait确认其确实因信号退出。重启时不会复用原进程内存、证明或去重集合。
4. 第二个全新进程使用相同消费者用途、队列和订阅,在原交付真实租约到期后收到补发。它读取业务数据库的原Inbox,Duplicate=true,然后ACK新证明并正常退出。
5. 最终event_id、delivery_id保持不变,平台attempts=2并ACKED,业务Inbox仍1行、applied_events仍1。
测试屏障只存在于测试二进制;正常示例CLI不提供故障开关。已经提交的业务结果不因强杀而回滚,恢复依靠持久化Inbox,而非把新进程视为从未执行过。
## 6. 剩余边界与计划
- Inbox保证本例SQL事务内的原子去重与版本推进,不涵盖邮件、外部HTTP等业务副作用;需要相应幂等和接收方Outbox设计。
- 核心SDK仍只有网络协议依赖,业务表不进入平台迁移。正式接收方需要自己的DDL升级、权限、加密、容量、保留及备份恢复方案。
- 历史清理、凭据重绑、全量监控告警、MySQL故障切换和生产容量/RPO/RTO仍按P6实施。
- 已重新核对[原功能范围](../01-functional-spec.md)与[事务衔接要求](../04-reliability-and-data.md),将尚缺的批量受理、独立任务核查/人工重试、业务键/时间筛选与业务Outbox接入明确列回[持续计划](../08-development-plan.md)。P3及整个平台均未宣称最终完成。