Files
orbit/docs/reports/phase-3-delay-recovery.md

75 lines
7.3 KiB
Markdown
Raw 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 相对延迟与重试全停恢复回归
验证日期:2026-09-17。本轮从[原范围核对](phase-3.md)发现并补齐相对等待时长的接口,同时补足“重试退避期间所有服务退出”的真实进程证据。全平台目标仍包含P4–P7,未宣称生产高可用或容量验收。
## 1. 实现与兼容
- 服务端和独立SDK新增可选delay_ms,默认0;首次受理在同一事务按数据库时间计算available_at,原等待时长参与请求摘要,重报先返回原身份回执,不重新计时。
- delay_ms小于任务总时限,非零不能与available_at同用;start_deadline_at仍校验计算后的最早执行时间。类型/溢出/非法组合明确拒绝。
- 零值omitempty维持历史请求摘要;[兼容测试](../../internal/storage/mysql/task_delay_test.go)以新增字段前的规范化JSON形状计算摘要作比较。批量复用同一受理逻辑;Outbox保留时长,不在业务发送端换算。
- 计划模板明确拒绝非零delay_ms,由已发布规则决定时间;人工重试使用原有新任务时限/绝对时间参数,不重新继承首次延迟。
- 不新增迁移、外键或持久化字段;仍为35个平台模型/289个直接持久化字段。新增命令字段有中文说明,执行后的退避继续使用既有available_at。
- [延迟SDK示例](../examples/delayed-task.md)执行实际Worker并查询开始时间;[查询示例](../examples/task-query.md)增加不同参数共享业务关联键及原输入保留核对。接口与持续计划同步。
## 2. 本轮测试的实质断言
| 测试 | 核对内容 |
| --- | --- |
| 规范化/解码 | 小于总时限边界、最大整数拒绝、相对与绝对互斥、null/负数/小数/字符串/溢出/重复字段拒绝;规范化不能提前将相对时间解析为客户端时刻 |
| HTTP/MySQL受理 | available_at精确等于created_at+5000ms;8个同键并发返回同一ID及原时间;改变时长或改为对应绝对时刻冲突;错误时间关系不受理;显式0兼容旧请求;批量每项使用自己的首次受理时间 |
| 真Worker延迟 | 相对1500ms及绝对时刻两项任务实际完成,尝试创建和开始时间均不早于保存的available_at;等待经过后原键重报仍取原回执,已完成任务不重开 |
| Outbox | 冻结文档保留delay_ms及精确数字,不创建available_at,不改变数据库依赖边界 |
| 改参数的关联任务 | 相同business_key、不同提交身份与不同format,GetTask确认两份输入各自保留;查询后取消三个样本,保留审计 |
进程测试:[TestIntegrationWorkerProcessRetryBackoffRecovery](../../sdk/go/retry_process_integration_test.go)。四个子场景都先确认第一次结果已入库并进入WAITING,保存完整旧尝试;Process.Kill终止唯一Orbit节点,Wait确认信号退出,然后启动两个不同node_id的新进程。
1. 明确可重试失败:8秒上限、半区间抖动,重启后完整任务快照不变;两个节点竞争只取得一次Epoch=2,实际领取时间不早于旧available_at,最终成功。
2. IDEMPOTENT未知效果:第二次明确可重试失败恰好耗尽2次预算,保留effect_uncertain,进入NEEDS_REVIEW,不能伪装为普通失败或创建第三次尝试。
3. 明确失败后退避,停机跨过5秒总时限:重启后FAILED,只保留首次尝试,不因恢复开启第二次。
4. 未知效果后退避,停机跨过总时限:重启后NEEDS_REVIEW且不确定性保留,同样只有首次尝试。
全部场景再次读取旧尝试,逐字段确认结果/分类/结束时间未被改写;核对冻结策略、总截止、输入、已用次数及队列未终结数。新节点保持在线并有容量,短窗口再次读取分配确认没有额外尝试。测试不修改数据库的available_at、deadline_at、lease_until;到期自然流逝,时间断言使用数据库记录。它验证进程/数据库逻辑,不验证跨故障域复制或外部系统副作用。
## 3. 环境、命令与实际结果
Darwin 24.6.0 arm64、12核/32 GiB宿主、Go 1.27.1、Docker内MySQL 8.4.10单实例。此硬件描述不是压测配置或容量结论。所有数据库为脚本创建的一次性容器,随机密码/回环端口,凭据仅存在私有临时文件;强杀仅影响测试拥有的子进程。
```sh
make fmt test vet build
make integration
```
make integration在停止初始服务后执行进程组,避免残留恢复节点掩盖接管;本轮新增测试已包含在原WorkerProcess匹配规则,新增delayed-task示例也进入正式脚本。
定向进程组在隔离库中使用已编译服务和专用Worker凭据执行:
```sh
cd sdk/go
go test -race -count=3 \
-run '^TestIntegrationWorkerProcessRetryBackoffRecovery$' -v .
```
需要隔离环境提供ORBIT_TEST_MYSQL_DSN、ORBIT_TEST_SERVER_BINARY、ORBIT_TEST_WORKER_URL、ORBIT_TEST_WORKER_CREDENTIALS_FILE;未提供时测试跳过,不能算数据库/进程证据。
| 执行 | 实际结果 |
| --- | --- |
| 四种进程场景重复3轮 | PASS,33.42 / 33.05 / 37.18秒,总包104.892秒,命令终态0;随后新增delay_ms再通过全量组验证 |
| 格式/竞态单元/静态检查/四模块构建 | PASS,make终态0;未设集成环境时的Integration跳过不计入真实数据库证据 |
| 全量集成/全部示例/进程组 | PASS,正式脚本终态0,18个核心SDK示例加Inbox/Outbox共20个,10个SDK节点/Worker进程测试(新增测试含4场景),外加Inbox/Outbox各1个进程故障测试 |
全量组记录:存储105.663秒;Worker91.765秒(新增相对/绝对延迟4.10秒);计划6.881秒;队列4.506秒;批量1.502秒;查询2.972秒;订阅管理33.485秒;事件消费54.737秒;人工恢复3.358秒;Inbox34.630秒;Outbox33.802秒;应用配额1.322秒;最终进程组95.151秒(新增四场景33.15秒)。所有组均完成,未根据中途PASS推断脚本结束。结束后确认脚本创建的容器已清理。
新增例子实际输出state=SUCCEEDED、attempts=1、no_early_start=true、original_receipt=true、worker_drained=true;改参数查询例子输出original_payload_preserved=true,均由程序断言后产生。SDK在GOWORK=off依赖图中无服务端、模型或MySQL依赖。
本轮没有发现需修改执行内核的故障;相对延迟是原范围核对发现的遗漏实现。新增回归和示例在同一次最终全量脚本执行前已经定稿,后续只更新文档证据。
本次修改前另执行零外键专项回归:MySQL 8.4.10、新建及v7升级、归属关系校验共11.738秒,终态0;本轮全量再次通过对应回归。所有耗时仅为回归耗时,不是性能指标。
本地执行日志位于/tmp/orbit-retry-process-focused.log、/tmp/orbit-delay-check-final.log及/tmp/orbit-delay-integration.log;临时日志不是SDK或部署依赖。本报告保留关键结果与可复现入口。
## 4. 限制
- delay_ms相对Orbit成功受理事务,不相对客户端发起或业务Outbox创建。可执行时间是下界,排队/暂停/故障可能推迟实际开始。
- 旧服务不支持该输入字段,启用前先升级服务;没有据此宣称混合版本滚动升级通过。
- 本轮没有MySQL主从切换、备份恢复、持续负载、重连风暴或生产容量数据;完整首版仍按[阶段核对](phase-3.md)与[持续计划](../08-development-plan.md)推进。