Files
orbit/docs/reports/phase-3-schedules.md

97 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.
# P3 计划调度阶段记录
验证日期:2026-09-17。已交付计划管理与持久化调度闭环,P3 仍有运行中取消、配额/背压、可靠订阅待完成;本报告不替代全平台生产验收。
## 1. 已实现行为
- 五段 Cron 与 fixed-rate;显式时区、开始和结束边界,固定间隔保持基准,不按任务完成时间漂移。
- 时区快照随定义冻结,服务从内置数据加载,不依赖宿主机或 ZONEINFO;兼容已有快照是后续升级约束。
- 创建、队列分页、详情、完整版本发布、历史版本、暂停/恢复、触发历史。每个命令有当前权限校验、幂等回执与管理审计。
- SKIP / COALESCE / CATCH_UP 漏跑策略与 PARALLEL / SERIAL / SKIP 重叠策略;明确补偿窗口、延迟容差、批量和扫描间隔。
- 发布新版本保留旧生效边界前的积压;跳过和合并范围不越过版本边界。已产生任务保留当时模板。
- 多节点用数据库锁与唯一触发身份竞争;触发决定、对应任务、事件及进度一起提交。
- 实际 orbit 命令持续扫描,独立 SDK 与 [完整周期快照示例](../../sdk/go/examples/schedules/main.go) 同步交付。
- 迁移 000006 增加运行字段与索引,实体仍为 32 个;新模型字段及 SQL COMMENT 均为中文;源码核对 32 个持久化模型、253 个直接声明的持久化字段均有中文说明。
完整字段、权限、状态与示例见 [接口手册](../api/schedules.md)。
## 2. 环境、命令及结果
macOS 本机,Go 1.27.1,Docker 单实例 MySQL 8.4.10。临时随机凭据、回环随机端口,运行结束清理容器;不使用业务数据库。
```sh
make fmt
make test vet build
make integration
cd sdk/go
GOWORK=off go test -race ./...
GOWORK=off go vet ./...
```
上述检查均已通过。未配置集成环境时,普通 go test 会跳过真实数据库/进程用例;实际证据来自 make integration。
最终完整集成输出:
| 测试组 | 本次结果 |
| --- | --- |
| MySQL 全部集成 | PASS,44.712 秒 |
| 计划事务与管理子集 | PASS,31.62 秒 |
| SDK Worker 连接/运行器及安全重试 | PASS,62.912 秒 |
| SDK 计划生命周期 | PASS,4.43 秒,含包启动/竞态开销 5.675 秒 |
| 真实进程故障组 | PASS,21.487 秒 |
| 新增调度进程强杀恢复 | PASS,10.23 秒 |
| 七个可运行示例 | health / tasks / worker-protocol / worker / history / retry / schedules 全部通过 |
时间为一次测试运行耗时,不能用作吞吐、延迟或容量指标。
## 3. 时钟与规则证据
[规则测试](../../pulse/schedule/rule_test.go) 验证:五段/分钟边界、亚洲时区、纽约缺失/重复小时、Lord Howe 半小时夏令时变更、Apia 整天跳过、2096→2104 世纪闰日、月日/星期 OR、固定间隔微秒和跨 292 年计算、结束排除边界、规则副本隔离及无解日期拒绝。
[时区测试](../../internal/timezone/zone_test.go) 核对内置快照完整 SHA-256、禁用外部 ZONEINFO 后仍能加载、精确名称及缓存,以及未知快照拒绝。快照来自 Go 1.27.1,附 Go 许可。Cron 库版本固定为 v3.0.1,只用于解析和 UTC 日历计算,运行不依赖外部调度服务;SDK 未新增该依赖。
[有限决策测试](../../pulse/schedule/planner_test.go) 覆盖补偿窗口截断、跳过、合并、有限逐点补跑、串行阻塞、重叠跳过、未来点不处理,以及合并不越过版本生效边界。计算器不自行读取系统当前时间,执行使用资源锁定后的数据库时间。
## 4. 实际数据库与 SDK 证据
[数据库用例](../../internal/storage/mysql/schedule_integration_test.go):
- 两个不同节点身份并发扫描同一到期计划,只创建本批三个唯一任务;再次扫描不能绕过持久化冷却时间。
- 禁用命名空间后,即使计划已到期也不再产生任务。
- 创建原键重报、同键不同内容冲突、暂停禁止触发、恢复原回执、跨命名空间/队列范围拒绝。
- 暂停期间发布新模板,旧积压仍用旧版本;历史版本不可被新模板覆盖,旧点处理后游标推进到新版本。
- 连续发布三个有限版本:第二版本尚待初始化时再次发布,仍保存其积压;空历史版本之后的末版全部处理完时,不重新扫描已完成触发点。新增用例先复现了漏掉中间版本和重复写入冲突,再验证修复。
- 超过时间窗口的跳过范围、漏跑合并范围与任务关联实际落库,范围结束边界大于起点。
- SERIAL 在待执行任务存在时等待;明确取消前项后继续;前项进入 UNKNOWN / NEEDS_REVIEW 后再次停止推进。
- 通过测试触发器让 occurrence 写入失败,任务、触发记录及游标整体回滚;故障解除后正常提交。
- 触发游标分页无重复,不能跨计划复用游标;任务模板中的大整数文本保留。
上述用例以真实时间等待形成积压,不把原触发时间改到计划创建前。少量用例将受控行 check_at 设为当前时间,以加速核对连续历史版本或阻塞状态;冷却限制另有未改写 check_at 的竞争测试。
[SDK 用例](../../sdk/go/schedule_integration_test.go) 连接实际服务,通过独立 SDK 创建暂停计划、幂等核对、恢复、自动 Worker 成功、暂停、发布 Cron 新版本、读取两个版本、原发布回执重报、列表及暂停期间不新增触发。业务结果中的 `9007199254740993` 保持精确。
## 5. 实际进程强杀恢复
新增 [TestIntegrationWorkerProcessScheduleRecovery](../../sdk/go/worker_process_integration_test.go) 使用构建后的 orbit 可执行文件和临时 MySQL:
1. 节点 A 创建一个每秒触发、共八个原触发点的有限计划,CATCH_UP / PARALLEL,每批上限二。
2. 观察到首个触发已经落库后,向 A 发送真实强杀并 Wait 确认退出。
3. 保留三秒无 Orbit 服务的间隙形成漏跑,不修改数据库时钟或计划游标。
4. 启动身份不同的 B、C 两个真实服务进程,从持久化进度竞争接续。
5. 查询至计划结束,严格核对恰好八条 FIRED、八个不同 TaskID、原 scheduled_at 序列及相同版本;每条都有完整 WAITING 任务与原模板。
这证明了本地单 MySQL 下服务进程消失后的持久化接续与多进程触发去重。本用例不启动业务 Worker,不宣称验证外部副作用 exactly-once。原开始/完成 ACK 丢失后强杀接管、Worker 进程中断转核查等 P2 用例也同步通过。
## 6. 可运行场景与当前边界
周期快照示例输出两条不同的 occurrence/task 身份,均完成 SUCCEEDED,随后 `paused=true completed_tasks=2 current_version=1`、`worker_drained=true running=0`。示例不访问外部业务资源;计划有十秒结束边界,正常完成后暂停,历史保留。
仍需后续验证或开发:
- 运行中任务取消、细粒度限流与队列配额、可靠结果订阅;流程引擎及人工核查控制。
- 调度期间数据库故障切换、提交 ACK 丢失的专门故障矩阵、跨主机网络分区、备份恢复和混合版本升级。
- 大表在线 DDL、历史人工计划行核查;000006 不自动启用旧的未绑定队列行。新建数据库与既有 v4 数据升级到当前结构的通用测试通过,但这不是旧人工计划内容的完整迁移验收。
- 大规模公平性、行锁竞争、重试/补跑风暴、历史保留与清理、租户配额。当前批量、扫描间隔和参数复制限制只建立有界基线,没有百万租户容量结论。
后续继续 [持续计划](../08-development-plan.md),不将本阶段闭环标为整个 P3 或最终目标完成。