# 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及整个平台均未宣称最终完成。