Files
orbit/docs/reports/phase-4-5-races.md

75 lines
7.6 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.
# P4/P5 版本、暂停竞争与全停恢复回归
日期:2026-09-18。对应[原故障矩阵](../05-production-acceptance.md)中的W06、W09、W14和W15。本批新增可复现的数据库竞争和公共SDK进程故障测试,没有修改服务端执行规则、接口、模型或迁移。无外键约束及中文模型注释保持。
## 验证方式
[真实MySQL测试](../../internal/storage/mysql/workflow_races_integration_test.go)使用一次性数据库,创建独立应用和命名空间。竞争驱动先锁定应用额度行,启动实际服务事务,并通过`performance_schema.data_lock_waits`确认每个参与者都已经进入InnoDB锁等待,再释放锁。测试账户需要读取这些诊断表;生产运行账户无需新增该权限。
这种方式证明事务实际重叠,不以goroutine启动、请求发出或固定sleep代替。测试不声称穷尽所有线程调度;两个参与者交换入队次序,三个参与者遍历六种入队次序。合法结果以持久状态、控制回执和数据库时间为准。
[公共SDK进程测试](../../sdk/go/workflow_process_integration_test.go)不导入服务端模块或数据库驱动,所有发布、启动、控制、信号、执行和查询走HTTP/WSS。启动两个真实Orbit进程;对两个进程分别发送SIGKILL并Wait确认退出;原TIMER唤醒和POLL下一轮就绪时间均过去后,才启动两个新进程。新节点ID必须彼此不同,也不能复用旧节点ID。
## W06:发布新版本后继续执行旧版本
v1实例先进入SIGNAL等待,此时没有后继任务。随后给同一定义发布v2,改变任务处理器版本、实际输入和流程总时限。旧实例收到信号后实际领取、开始、完成后继任务,验证仍使用v1处理器、精确数字9007199254740993、原定义版本ID与原截止。
再显式启动v2、发送信号并实际完成任务,验证使用v2处理器和新输入7。Worker能力集合属于进程身份,因此为v2能力注册新Worker身份;不能改写旧身份的能力集合。两个实例共两个任务,最终许可释放。验证覆盖后继执行及输出,不只检查启动回执。
## W09:信号、自然截止与取消三方竞争
SIGNAL等待设置1秒原时限。信号受理、截止消费者和CANCEL三个事务都进入锁等待后,测试查询数据库时间,等其自然到达原截止再放行;不提前修改等待截止。
六种入队顺序各有两组:没有及时信号,以及此前已经持久受理一个及时信号。本次竞争的新信号必须在原截止后受理,只能保留为过期或拒绝,不能成为赢家。此前按时受理的信号仍必须被消费并成为唯一等待赢家,不能因为消费延迟或取消结清而丢失。
断言包括原截止不变、CANCEL意图保留、无悬挂PENDING信号、无任务执行尝试、无许可遗留、后继至多一个、同键重报返回原信号回执。截止先完成时可能保留NEEDS_REVIEW;取消先完成时可进入CANCELLED。测试保留这两种合法结果,不把取消当作解除已有核查的权限。
## W14:暂停与四种实际操作竞争
分别让PAUSE与Start、Claim、已确认结果的后继创建、已知失败的安全重试上报竞争,每组交换入队次序。以PAUSE回执的数据库时间核对任务创建、尝试创建和开始授权,禁止暂停后发生这些新工作。
- 已领取但未授权的尝试在暂停时Start必须拒绝;解除暂停后使用原尝试,不能再扣一次预算。
- 已授权的工作保留许可,暂停后仍能上报并收集结果,但不能创建后继;结果结清后释放许可。
- 重试上报可保留失败事实和原退避,暂停期间即使退避到期也不能领取下一次尝试。
- 解除暂停后从持久进度完成两个任务,普通场景恰好两个尝试,安全重试场景恰好三个尝试;原流程截止与原任务预算保持。
存储测试通过显式调用内部消费者推进。根待续采用已有集成测试辅助函数将扫描就绪时间置为当前时间,业务截止和重试退避不因此改变;完整后台扫描行为由下述真实进程场景验证。
## W15:暂停跨全部控制节点退出后恢复
并行分支包括一个已开始TASK、一个SIGNAL、一个TIMER和一个两轮POLL,汇合后还有一个TASK。第一轮查询取得PENDING后暂停实例;暂停期间发送信号并完成已授权任务,确认任务结果已提交,然后使两个原节点全部退出。
新节点接管后,公共SDK检查:
- 原任务结果、原信号受理时间/赢家和TIMER到期事实保留;等待ID、激活ID/代次及原截止保持。
- paused和control_revision保留,汇合后的TASK仍BLOCKED,未创建任务。
- POLL仍只使用第一轮,保留原下一轮时间、查询截止和2轮/每轮2次预算;停机不补发轮次。
- 在另一新节点重报原START、PAUSE、SIGNAL,仍返回首次冻结回执。
- 通过另一新节点RESUME后,第二轮查询DONE,再完成汇合后的TASK。最终按业务键查到恰好四个任务,每个恰好一次尝试;轮次历史恰好两轮,原版本、输出、截止及首任务策略不变。
- 成功后重报原RESUME仍返回控制版本3,不创建第二次恢复。
## 命令及验证记录
标准入口:
```sh
make fmt test vet build
make integration
```
`scripts/test-mysql.sh`会创建并清理隔离MySQL;存储组自动执行新的21个子场景。原HTTP服务退出、原Worker/订阅进程测试完成清理后,脚本用独立流程权限凭据单独执行`TestIntegrationWorkflowProcessPauseRecovery`,避免其他控制节点代为推进。
环境:macOS arm64、Go 1.27.1、Docker MySQL 8.4.10。初次定向回归发现测试夹具两处调用错误:修改旧Worker身份的能力集合,以及失败上报遗漏必填非null结果;平台按已有契约拒绝,夹具已改为新身份和合法失败结果。修正后存储定向测试退出0(24.062秒),公共SDK全停测试退出0(10.968秒)。基础fmt、race单元、vet、build退出0。
随后补强数据库授权时间、精确输入数字、全部任务数和完整轮次历史断言,并纳入标准全量回归。完整`scripts/test-mysql.sh`退出0:存储组249.189秒、原流程进程组48.751秒、Inbox34.678秒、Outbox34.688秒、Worker/订阅进程组95.466秒、新暂停全停组13.797秒;23个SDK示例和2个独立业务模块全部通过。这些数字是测试耗时,不是吞吐或延迟指标。
收尾给测试中的“尝试创建或授权时间”SQL条件补上括号,使两个条件都受命名空间限制;该测试修改晚于全量脚本的存储组执行。最终21个存储子场景另在新隔离MySQL、`-race -count=1`下完整重跑,退出0(24.732秒)。服务端、SDK运行代码及迁移均未变化;没有改变生产契约来迎合测试。
最终Markdown检查1173个本地链接、68个JSON块,错误0;格式和差异检查通过。全量升级回归继续确认70个历史外键移除、323个原索引组成部分和原任务保留,最终结构外键0。
## 阶段边界与后续
这四项补强不能代替全平台生产验收。原范围继续保留:W01/W13精确COMMIT结果不明切点、A04派发落库但消息尚未送达、MySQL失联/主从切换、跨故障域容量、热点锁等待、慢消费者及重连风暴、凭据轮换、保留归档、备份恢复和滚动兼容。
下一步按[P4/P5原范围核对](phase-4-5.md)汇总阶段证据,再推进P6统一故障注入和运行观测、P7 SDK发布及实际文件转换示例。当前进程强杀验证使用单机数据库,不提供生产HA、RPO/RTO或百万企业容量结论。