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

7.6 KiB
Raw Blame History

P4/P5 版本、暂停竞争与全停恢复回归

日期:2026-09-18。对应原故障矩阵中的W06、W09、W14和W15。本批新增可复现的数据库竞争和公共SDK进程故障测试,没有修改服务端执行规则、接口、模型或迁移。无外键约束及中文模型注释保持。

验证方式

真实MySQL测试使用一次性数据库,创建独立应用和命名空间。竞争驱动先锁定应用额度行,启动实际服务事务,并通过performance_schema.data_lock_waits确认每个参与者都已经进入InnoDB锁等待,再释放锁。测试账户需要读取这些诊断表;生产运行账户无需新增该权限。

这种方式证明事务实际重叠,不以goroutine启动、请求发出或固定sleep代替。测试不声称穷尽所有线程调度;两个参与者交换入队次序,三个参与者遍历六种入队次序。合法结果以持久状态、控制回执和数据库时间为准。

公共SDK进程测试不导入服务端模块或数据库驱动,所有发布、启动、控制、信号、执行和查询走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,不创建第二次恢复。

命令及验证记录

标准入口:

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原范围核对汇总阶段证据,再推进P6统一故障注入和运行观测、P7 SDK发布及实际文件转换示例。当前进程强杀验证使用单机数据库,不提供生产HA、RPO/RTO或百万企业容量结论。