feat: support durable task cancellation without foreign keys
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 数据模型与实现基线
|
||||
|
||||
更新:2026-09-17。当前已实现任务执行、安全重试与计划调度;剩余资源控制、订阅及流程编排继续开发。
|
||||
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消与计划调度;剩余资源控制、订阅及流程编排继续开发。
|
||||
|
||||
## 1. 模块与依赖
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
| Go 工具链 | Go 1.27.1;服务端和 SDK 分别编译测试 |
|
||||
| 标识 | 128 位加密随机 ID;数据库 `BINARY(16)`,对外 32 位小写十六进制字符串 |
|
||||
| 摘要 | SHA-256,数据库 `BINARY(32)`;执行令牌及凭据原文不进入模型 |
|
||||
| 租户隔离 | 运行实体以 `(namespace_id, id)` 为主键,具体资源关联使用包含命名空间的复合外键 |
|
||||
| 租户隔离 | 运行实体以 `(namespace_id, id)` 为主键;资源存在性、命名空间及父资源归属由服务端事务校验 |
|
||||
| 外键 | 禁止数据库外键;保留关联字段、查询索引、唯一约束和 CHECK |
|
||||
| 时间 | `DATETIME(6)`,数据库会话与驱动统一 UTC;运行授权依赖数据库时间 |
|
||||
| 状态 | Go 类型常量 + SQL `CHECK`;状态版本独立于时间戳 |
|
||||
| CAS | `revision` 从 1 开始;服务层必须带期望版本条件更新并检查受影响行数 |
|
||||
| 暂停 | 实例执行状态与 `paused` 分开,控制版本与通用门控同事务协调 |
|
||||
| 参数 | 版本化 JSON 文档或业务侧引用;业务内容使用 MEDIUMTEXT + JSON_VALID 保留数字精度,平台元数据使用原生 JSON |
|
||||
| 删除 | 关联数据默认 `RESTRICT`,没有级联删除执行历史 |
|
||||
| 删除 | 禁止级联删除;当前无公开硬删除接口,历史保留。后续清理须由服务端核验引用及保留窗口 |
|
||||
| 不可变历史 | 流程版本、计划版本、状态事件、审计事件的 `UPDATE` 被数据库触发器拒绝 |
|
||||
|
||||
随机 ID 的索引成本需要后续容量测试,不将该选择描述为已经验证的大规模吞吐保证。运行账户应最小授权,迁移与保留清理使用不同身份;触发器不是对数据库管理员的防篡改保证。
|
||||
@@ -76,18 +77,22 @@
|
||||
## 4. 已落实的数据库约束
|
||||
|
||||
- 幂等身份在命名空间、操作和资源作用域内唯一;保存请求摘要和原响应。
|
||||
- 一个任务的执行代次唯一,当前尝试必须属于同命名空间、同任务。
|
||||
- 一个任务的执行代次唯一;当前尝试归属由执行事务另行校验。
|
||||
- 计划触发按计划与 UTC 原触发时间唯一,计划修改不会绕过触发去重。
|
||||
- 定义版本不可覆盖,实例绑定明确的版本记录。
|
||||
- 步骤属于指定实例,激活属于指定步骤;避免同租户内错接另一个流程。
|
||||
- 定义版本不可覆盖;实例保存版本 ID,版本归属校验随流程引擎实现。
|
||||
- 步骤和激活保留父资源 ID 及关联索引,数据库不校验跨表归属。
|
||||
- 查询轮次与任务分别唯一;轮次、单轮尝试预算和截止时间独立保存。
|
||||
- 内部事件与外部订阅交付有去重键,租用状态要求有效租用字段。
|
||||
- 终态必须有结束时间,待执行任务不能伪造有效执行租约。
|
||||
- effect_uncertain=true 不能和成功、失败、取消终态共存;尝试/证据 retryable=true 只允许明确失败。
|
||||
|
||||
## 5. 仍需服务层事务保证的条件
|
||||
## 5. 服务层关联与事务规则
|
||||
|
||||
外键与 `CHECK` 无法代替执行引擎。P1 已完成 HTTP 动作/队列授权、幂等摘要与原回执、受理事件,以及待执行取消的 CAS/审计事务。执行相关的以下条件继续分阶段实现:
|
||||
数据库禁止外键,关联一致性由应用负责;CHECK 仅保证单行约束。任务提交、计划创建和 Worker 注册在同一事务中按 namespace_id 校验队列存在性,校验失败回滚所有新增记录。鉴权校验凭据、命名空间及所属应用;执行入口核验进程、会话、执行代次、任务和尝试归属。接口不能仅相信调用方传入的资源 ID。
|
||||
|
||||
禁止“先查后另开事务写入”。需要防止并发状态改变的关联行在事务内持锁,统一锁顺序并保留必要索引。当前不提供硬删除;将来增加清理时,必须核验在途任务、引用、幂等恢复窗口与审计保留期,按有限批次处理,不能依赖数据库级联。直接绕过服务写 SQL 不受应用校验保护。
|
||||
|
||||
P1 已完成动作/队列授权、幂等摘要与原回执、受理事件及取消 CAS/审计事务;P3 扩展到运行中取消。流程及订阅目前只具备模型,后续实现也必须补齐事务内父资源归属验证,不能把模型存在当作已完成引擎。
|
||||
|
||||
P2 服务端已新增会话隔离、两步派发、租约、并发许可、结果回执/证据和恢复扫描,见 [执行协议](api/worker.md)。底层及自动 Worker SDK、尝试/证据查询、Schema 与进程故障恢复已通过 [P2 阶段验收](reports/phase-2.md)。
|
||||
|
||||
@@ -103,7 +108,7 @@ P2 服务端已新增会话隔离、两步派发、租约、并发许可、结
|
||||
|
||||
## 6. 迁移与回归
|
||||
|
||||
入口是 `orbit-migrate up|status`。应用进程只检查迁移状态,不执行 DDL。迁移持有数据库级命名锁,逐语句应用;失败保留 dirty 标记。由于 MySQL DDL 不整体回滚,修复必须先对照语句和实际表结构完成核查,不提供自动清除 dirty 的命令。
|
||||
入口是 `orbit-migrate up|status`。应用启动及 readiness 检查迁移状态和 Orbit 表的外键数量,发现外键会拒绝启动/就绪,不执行 DDL。这不是全部手工结构漂移审计。迁移持有数据库级命名锁,逐语句应用;失败保留 dirty 标记。由于 MySQL DDL 不整体回滚,修复必须先对照语句和实际表结构完成核查,不提供自动清除 dirty 的命令。
|
||||
|
||||
已应用的迁移不得修改。后续使用新的前向迁移;回归环境使用独立临时数据库。初始迁移尚未在生产部署。
|
||||
|
||||
@@ -115,4 +120,8 @@ P2 服务端已新增会话隔离、两步派发、租约、并发许可、结
|
||||
|
||||
000006_schedule_runtime 增加 Schedule.QueueID / ProcessingVersion / CheckAt 以及 ScheduleVersion.CatchupWindowMS / GraceMS。字段、单位、可空语义均有中文注释;CoveredUntil 明确为合并或跳过范围的排除边界。实体数不变,增加扫描、列表和任务来源索引。时间规则 JSON 冻结 CalendarVersion,服务内置相应时区快照。调度记录、生成任务、事件和进度同事务写入,旧版本积压不会被新发布覆盖;详见 [计划契约](api/schedules.md)。
|
||||
|
||||
命令与验证记录见 [开发计划](08-development-plan.md)、[基础阶段验证报告](reports/phase-0.md) 与 [执行历史回归](reports/phase-2-execution-history.md)。
|
||||
000007_task_cancellation 允许尝试证据记录 CANCELLED,不新增实体;取消意图、结果和历史不确定效果的规则见 [取消契约](api/cancellation.md)。
|
||||
|
||||
000008_no_foreign_keys 移除历史 70 个外键,保留索引、唯一约束、CHECK、触发器和数据。旧迁移已在回归库应用,按不可变校验和约定保留原文件;新装按顺序运行至当前版本,最终结构同样为零外键。后续迁移禁止再引入外键。升级旧库时先停用旧服务,应用完整迁移再启动新版;迁移需要 DDL 锁,应在目标部署环境演练维护窗口。
|
||||
|
||||
命令与验证记录见 [开发计划](08-development-plan.md)、[基础阶段验证报告](reports/phase-0.md) 与 [取消及无外键回归](reports/phase-3-cancellation.md)。历史阶段报告描述当时结构,当前约束以本页为准。
|
||||
|
||||
Reference in New Issue
Block a user