14 KiB
数据模型与实现基线
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度、队列限速/背压和应用配额;优先级及节点公平领取已实现;订阅及流程编排继续开发。
1. 模块与依赖
model/:ID、摘要、JSON、命名空间记录等公共原语。platform/model/:应用、命名空间、凭据、幂等、事件、订阅交付及审计。pulse/model/:队列、执行门控、进程/会话、任务、尝试、回执、迟到证据、并发许可和调度计划。weave/model/:定义及不可变版本、实例、步骤激活、等待、信号、查询轮次与恢复记录。internal/storage/mysql/:连接、事务与显式迁移,不向 SDK 暴露。sdk/go/:独立 Go module;对外 DTO 与服务端模型分别维护。
模型使用普通 Go struct 与 db 标签,模型与持久化字段提供中文注释,SQL 表和字段同步维护中文 COMMENT。SQL 是可审查的版本化迁移。目前未引入 ORM,也没有自动同步表结构。
2. 存储约定
| 项目 | 当前实现 |
|---|---|
| 数据库基线 | MySQL 8.4+,本轮在 8.4.10 验证 |
| Go 工具链 | Go 1.27.1;服务端和 SDK 分别编译测试 |
| 标识 | 128 位加密随机 ID;数据库 BINARY(16),对外 32 位小写十六进制字符串 |
| 摘要 | SHA-256,数据库 BINARY(32);执行令牌及凭据原文不进入模型 |
| 租户隔离 | 运行实体以 (namespace_id, id) 为主键;资源存在性、命名空间及父资源归属由服务端事务校验 |
| 外键 | 禁止数据库外键;保留关联字段、查询索引、唯一约束和 CHECK |
| 时间 | DATETIME(6),数据库会话与驱动统一 UTC;运行授权依赖数据库时间 |
| 状态 | Go 类型常量 + SQL CHECK;状态版本独立于时间戳 |
| CAS | revision 从 1 开始;服务层必须带期望版本条件更新并检查受影响行数 |
| 暂停 | 实例执行状态与 paused 分开,控制版本与通用门控同事务协调 |
| 参数 | 版本化 JSON 文档或业务侧引用;业务内容使用 MEDIUMTEXT + JSON_VALID 保留数字精度,平台元数据使用原生 JSON |
| 删除 | 禁止级联删除;当前无公开硬删除接口,历史保留。后续清理须由服务端核验引用及保留窗口 |
| 不可变历史 | 流程版本、计划版本、状态事件、审计事件的 UPDATE 被数据库触发器拒绝 |
随机 ID 的索引成本需要后续容量测试,不将该选择描述为已经验证的大规模吞吐保证。运行账户应最小授权,迁移与保留清理使用不同身份;触发器不是对数据库管理员的防篡改保证。
3. 实体清单
当前共 33 个实体:初始迁移包含 30 个,000003 增加进程会话头和迟到证据,000010 增加应用共享额度。迁移自身另有 orbit_schema_migrations 记录。
| 模块 | Go 模型 | 数据表 |
|---|---|---|
| platform | Application |
orbit_applications |
| platform | ApplicationResources |
orbit_application_resources |
| platform | Namespace |
orbit_namespaces |
| platform | Credential |
orbit_credentials |
| platform | IdempotencyKey |
orbit_idempotency_keys |
| platform | Event |
orbit_events |
| platform | Subscription |
orbit_subscriptions |
| platform | Delivery |
orbit_deliveries |
| platform | AuditEvent |
orbit_audit_events |
| pulse | Queue |
pulse_queues |
| pulse | ExecutionGate |
pulse_execution_gates |
| pulse | WorkerSession |
pulse_worker_sessions |
| pulse | WorkerInstance |
pulse_worker_instances |
| pulse | Task |
pulse_tasks |
| pulse | Attempt |
pulse_attempts |
| pulse | AttemptReceipt |
pulse_attempt_receipts |
| pulse | AttemptEvidence |
pulse_attempt_evidence |
| pulse | ConcurrencyLimit |
pulse_concurrency_limits |
| pulse | Permit |
pulse_permits |
| pulse | Schedule |
pulse_schedules |
| pulse | ScheduleVersion |
pulse_schedule_versions |
| pulse | Occurrence |
pulse_schedule_occurrences |
| weave | Definition |
weave_definitions |
| weave | DefinitionVersion |
weave_definition_versions |
| weave | Instance |
weave_instances |
| weave | Step |
weave_steps |
| weave | Activation |
weave_activations |
| weave | Wait |
weave_waits |
| weave | Signal |
weave_signals |
| weave | PollState |
weave_poll_states |
| weave | PollRound |
weave_poll_rounds |
| weave | InboxEvent |
weave_inbox |
| weave | PendingAdvance |
weave_pending_advances |
设计文档中的 task_events / result_events 当前合并为 orbit_events,通过聚合类型、事件类型和状态版本区分;对外投递使用 orbit_deliveries,内部流程接续使用 weave_inbox。weave_pending_advances 保存暂停或分批推进后的续接检查点。
4. 已落实的数据库约束
- 幂等身份在命名空间、操作和资源作用域内唯一;保存请求摘要和原响应。
- 一个任务的执行代次唯一;当前尝试归属由执行事务另行校验。
- 计划触发按计划与 UTC 原触发时间唯一,计划修改不会绕过触发去重。
- 定义版本不可覆盖;实例保存版本 ID,版本归属校验随流程引擎实现。
- 步骤和激活保留父资源 ID 及关联索引,数据库不校验跨表归属。
- 查询轮次与任务分别唯一;轮次、单轮尝试预算和截止时间独立保存。
- 内部事件与外部订阅交付有去重键,租用状态要求有效租用字段。
- 终态必须有结束时间,待执行任务不能伪造有效执行租约。
- effect_uncertain=true 不能和成功、失败、取消终态共存;尝试/证据 retryable=true 只允许明确失败。
5. 服务层关联与事务规则
数据库禁止外键,关联一致性由应用负责;CHECK 仅保证单行约束。任务提交、计划创建和 Worker 注册在同一事务中按 namespace_id 校验队列存在性,校验失败回滚所有新增记录。鉴权校验凭据、命名空间及所属应用;执行入口核验进程、会话、执行代次、任务和尝试归属。接口不能仅相信调用方传入的资源 ID。
禁止“先查后另开事务写入”。需要防止并发状态改变的关联行在事务内持锁,统一锁顺序并保留必要索引。当前不提供硬删除;将来增加清理时,必须核验在途任务、引用、幂等恢复窗口与审计保留期,按有限批次处理,不能依赖数据库级联。直接绕过服务写 SQL 不受应用校验保护。
P1 已完成动作/队列授权、幂等摘要与原回执、受理事件及取消 CAS/审计事务;P3 扩展到运行中取消。流程及订阅目前只具备模型,后续实现也必须补齐事务内父资源归属验证,不能把模型存在当作已完成引擎。
P2 服务端已新增会话隔离、两步派发、租约、并发许可、结果回执/证据和恢复扫描,见 执行协议。底层及自动 Worker SDK、尝试/证据查询、Schema 与进程故障恢复已通过 P2 阶段验收。
- 权限验证、凭据校验、命名空间过滤和队列消费授权。
- 同一次事务更新任务、尝试、租约、并发许可、事件与流程交付记录。
- CAS 竞争、数据库时间校验、门控与开始授权串行化。
- 对幂等重复请求比较语义摘要,返回原响应或冲突。
- 计划当前版本与版本内容原子发布,信号匹配正确的业务操作。
- 查询轮次、预算、下一任务创建同事务提交;未知结果按证据核查。
- 不可变版本发布前的流程图校验、表达式类型与结构化汇合验证。
局部模型方法不能被当作已经实现了 Worker 执行授权和业务效果幂等。P1 已实现范围与剩余边界见 P1 报告。
6. 迁移与回归
入口是 orbit-migrate up|status。应用启动及 readiness 检查迁移状态和 Orbit 表的外键数量,发现外键会拒绝启动/就绪,不执行 DDL。这不是全部手工结构漂移审计。迁移持有数据库级命名锁,逐语句应用;失败保留 dirty 标记。由于 MySQL DDL 不整体回滚,修复必须先对照语句和实际表结构完成核查,不提供自动清除 dirty 的命令。
已应用的迁移不得修改。后续使用新的前向迁移;回归环境使用独立临时数据库。初始迁移尚未在生产部署。
000002_task_api 增加任务分页索引,将业务参数、结果、回执、流程定义/输入/输出等字段改为校验过的 JSON 文本,避免原生 JSON 数字重编码。公共 Go JSON 扫描类型保持兼容,原模型职责不变。响应 DTO 与持久化模型分开;SQL 空结果先扫描到可空字节切片再转换,避免驱动与 json.RawMessage 的可空类型不兼容。
000004_execution_history 为已有证据表增加 (namespace_id, attempt_id, created_at, id) 分页索引,不新增实体或字段,不回改已有迁移。尝试列表复用执行代次唯一索引。查询 DTO 与持久化模型分离,只在详情读取业务正文,不返回执行秘密或内部摘要;接口见 执行历史。
000005_safe_retry 增加任务 EffectUncertain、尝试与证据 Retryable 字段,Go 与 SQL 均附中文说明;实体数仍为 32。跨尝试的不确定效果必须保留,历史 NEEDS_REVIEW 保守回填 true,旧状态、版本和冻结策略不变。回执增加 (namespace_id, attempt_id, operation, request_digest) 索引,换消息身份重报结果也返回原已应用回执。字段语义、升级策略启用与限制见 安全重试,升级回归见 阶段报告。
000006_schedule_runtime 增加 Schedule.QueueID / ProcessingVersion / CheckAt 以及 ScheduleVersion.CatchupWindowMS / GraceMS。字段、单位、可空语义均有中文注释;CoveredUntil 明确为合并或跳过范围的排除边界。实体数不变,增加扫描、列表和任务来源索引。时间规则 JSON 冻结 CalendarVersion,服务内置相应时区快照。调度记录、生成任务、事件和进度同事务写入,旧版本积压不会被新发布覆盖;详见 计划契约。
000007_task_cancellation 允许尝试证据记录 CANCELLED,不新增实体;取消意图、结果和历史不确定效果的规则见 取消契约。
000008_no_foreign_keys 移除历史 70 个外键,保留索引、唯一约束、CHECK、触发器和数据。旧迁移已在回归库应用,按不可变校验和约定保留原文件;新装按顺序运行至当前版本,最终结构同样为零外键。后续迁移禁止再引入外键。升级旧库时先停用旧服务,应用完整迁移再启动新版;迁移需要 DDL 锁,应在目标部署环境演练维护窗口。
000009_queue_resources 增加 Queue.MaxUnfinishedTasks、UnfinishedTasks、StartWindowUntil、StartsInWindow,Go 字段与 SQL COMMENT 均有中文语义,实体仍为 32。未终结任务按 WAITING/LEASED/RUNNING/NEEDS_REVIEW 回填;窗口初始为空、计数为零。运行计数与任务/开始授权同事务维护,不改变队列配置 revision,配置变更不能重置它们。降额允许已有使用量暂时超过新上限,不删除任务或撤销已有许可;详见 队列契约 和 资源回归。
命令与验证记录见 开发计划、基础阶段验证报告 与 取消及无外键回归。历史阶段报告描述当时结构,当前约束以本页为准。
12. 应用共享额度
000010_application_resources 新增 ApplicationResources,使用 BINARY(16) 应用主键,12 个持久化字段均有中文注释和 SQL COMMENT,无外键。共享未终结任务数、活跃尝试数、未开始预占数与一秒开始窗口;配置 revision 不随用量变化。停止旧写入节点后回填所有应用,包含跨命名空间的既有在途状态。关联身份行与额度行分开,认证后按应用额度 → 队列 → 任务/尝试 → 许可顺序持锁。后台恢复不因应用停用而停止释放到期尝试。详细字段、权限、SDK 场景及并发边界见 应用配额接口。
13. 显式资源键许可
000011_keyed_concurrency 增加 Task.ConcurrencyKey / ConcurrencyKeyDigest、Attempt.ConcurrencyToken、WorkerSession.ConcurrencyKeys,共四个中文注释与 SQL COMMENT 字段。历史值分别为空/空、零和 false,原 BusinessKey 语义不变;截至 000011 为 33 个实体、273 个直接声明的持久化字段,最终外键数保持零。
资源限制复用 ConcurrencyLimit:scope_key=key,key_digest 为域分隔 SHA-256,命名空间内唯一;每键最大许可固定 1。原始 UTF-8 字节精确匹配,不依赖 MySQL 的文本排序规则。一个有键尝试持有队列和资源键两条 Permit,应用活跃尝试只计一次;续租、释放、恢复与状态同事务完成。
键行 Revision 在领取和释放时递增,领取后的值保存到 Attempt.ConcurrencyToken;同尝试重连不改变序号,资源行不得删除或重置。达到整数边界时拒绝新领取,禁止回绕。SDK 交付序号,资源端负责原子比较与业务幂等;历史未知效果与执行许可释放分别维护。字段、升级、调用和异常语义见 资源键契约。
14. 任务优先级和节点公平领取
000012_dispatch_priority 开放既有 Priority,范围 0–9,并增加对应 CHECK。新增只读生成列 DispatchAt(AvailableAt 减 Priority 乘30秒)及 (namespace_id,queue_id,state,dispatch_at,available_at,id) 排序索引;历史默认优先级 0 的排序时间等于 AvailableAt。Generated 字段随退避和恢复自动重新计算,不允许独立写入。33 个实体、274 个直接声明的持久化字段全部保留中文注释,新增 SQL 字段和 Priority 的 COMMENT 同步更新,无外键。
派发先检查实际到期、截止、能力、门控和资源键,再按排序时间选择。节点公平领取池根据认证后的 ApplicationID 分组,再按命名空间/队列轮转;内存队列和短期空探测缓存可在重启后重建,不保存授权事实。详细约束、SDK 和升级步骤见 优先级与公平派发。