Files
orbit/docs/13-workflow-clock.md

66 lines
6.9 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.
# Weave 持久化定时等待与查询轮次
状态:TIMER到期推进和POLL后续业务轮次已接入Orbit后台运行器。复用MySQL和既有待续索引,不增加外部调度服务或数据库外键。[SIGNAL消费与查询最终信号竞争](14-workflow-signals.md)、内部控制及停止结清、实例公共HTTP/SDK和人工恢复均已接入;对应恢复操作见[恢复接口](api/workflow-recovery.md)。本页只说明持久化时钟语义,生产调度容量仍需按交付清单验收。
## 1. 时钟和恢复身份
`weave_pending_advances`同时保存ROOT、TIMER、SIGNAL、POLL四类检查点。唯一范围为命名空间、实例和步骤;时钟检查点额外固定当前激活ID,避免旧激活唤醒新工作。扫描只提供候选,实际推进仍须持锁核验固定版本、父记录、步骤/激活状态、任务所有者、门控和原始预算。
| 检查点 | 到期时间来源 | 推进结果 |
| --- | --- | --- |
| ROOT | 分批推进或暂停/满额后的下一检查时间 | 重新计算最多32项步骤变更 |
| TIMER | 激活时保存的等待截止 | 完成原等待或记录超期核查,再有限推进 |
| POLL当前轮次仍在途 | 查询节点的原绝对截止 | 保留结果收集或关闭新执行门控,不启动重叠轮次 |
| POLL上一轮业务PENDING | 上一轮权威结果发生时间加固定间隔,受查询截止限制 | 若允许且有额度,同事务创建下一轮任务 |
每个候选遵守[运行器事务顺序](12-workflow-runtime.md):身份、应用额度、排序队列、实例、门控和子记录。多个Orbit节点竞争同一数据库记录,不能凭本地时间或内存缓存取得推进权。扫描失败回滚并按候选版本延后重查,既有结果和预算仍保留。
TIMER由待续表直接唤醒,不创建Pulse业务任务,也不占Worker执行许可。旧模型的`wake_task_id`当前保持空;后续若改变这一约定,需要独立迁移与兼容验证。
## 2. 定时等待
激活时保存开始时间及`min(开始时间 + delay_ms, 实例总截止)`。恢复时重新验证这条等式,不能从重启或继续时间重新计时。
- 原计划等待时长能在实例总截止内结束:到期保存SATISFIED及步骤SUCCEEDED,输出为JSON `null`。
- 后续步骤才激活TIMER,导致剩余实例时长不足:到总截止保存EXPIRED、步骤及实例NEEDS_REVIEW,不能把被截短的等待当作成功。
- 暂停期间已到期的等待仍可收集完成;后继保留ROOT待续,直到控制状态允许。暂停不延长截止。
- 全部进程停止后才被扫描:使用原到期时间。若整个流程已能由有效的到期事实完成,可以整理完成;若还需启动新步骤且实例总截止已过,则进入核查。
等待结清、步骤状态、后继任务或ROOT检查点、事件和审计同事务。审计失败不会留下已完成等待或部分后继。
## 3. 有界查询与请求重试
一次业务查询轮次对应一个Pulse任务。当前任务成功返回PENDING后,内部结果事务保存WAITING、清除`current_task_id`、记录`next_poll_at`并更新POLL检查点。成功查询仍在处理和请求执行失败使用不同预算:
| 场景 | 业务轮次 | Pulse尝试 |
| --- | --- | --- |
| 已知可重试请求失败 | 不增加,仍为原任务 | 按该任务冻结策略增加 |
| 查询成功返回PENDING且未耗尽预算 | 到固定间隔后创建下一任务,增加1 | 新一轮从自身首次尝试开始 |
| DONE等声明成功结果 | 结清当前查询节点 | 不再启动下一轮 |
| 单轮失败/效果未知/非法结果/业务核查值 | 停止正常推进并保存核查事实 | 不通过创建新轮次绕过原尝试上限 |
| 轮次耗尽或总截止到达 | 不增加,进入核查 | 原在途任务仍需收集实际结果 |
创建下一轮前校验轮次连续、当前无在途任务、上一轮已经确认为POLL_PENDING。复用首轮已解析的精确输入、业务键、资源互斥键以及固定版本的处理器、契约、优先级和重试策略;不重新解释已完成步骤的表达式。任务绝对截止仍取自身时限、查询总截止和实例总截止的较早者。
新任务、所有者和门控、轮次记录、已用轮数、当前任务、步骤状态、下一检查点、事件及审计同事务。插入轮次或审计失败会连同任务及额度一起回滚。应用/队列满额、暂停或身份停用仅延后检查;不消耗新轮次,不改变原截止。
## 4. 截止与已落库结果的竞争
截止扫描发现当前任务已经有终态或核查结果,但Inbox还未消费时,保留时钟并优先等Inbox按原事件时间裁决。不能用扫描发生时间否定已按时可靠受理的成功结果。
当前任务仍在途且查询总截止已到时,实例转NEEDS_REVIEW并关闭新执行门控;步骤保持ACTIVE、轮次保持当前任务引用,继续收集Pulse结果。时钟不释放执行许可,也不假定外部请求已经停止。Pulse恢复将失联执行记录为UNKNOWN后,内部消费再结清当前查询记录,保留核查状态。
最终信号与在途查询的竞争已按[信号运行约定](14-workflow-signals.md)接入:保存赢家后等待Pulse查询结清,不能提前释放许可或覆盖未知效果。实例及信号公共HTTP/SDK已接入,见[实例接口](api/workflows.md)。
## 5. 升级和验证
[迁移000019](../internal/storage/mysql/migrations/000019_workflow_clock_checkpoints.up.sql)仅回填旧实例当前激活的TIMER/POLL检查点:定时等待使用原deadline,等待查询使用原next_poll_at,在途查询使用原查询deadline。已有检查点不覆盖,终态实例及旧激活不回填;不新增表、字段、索引、外键或级联。
升级顺序为停止旧写入节点 → 应用全部迁移 → 启动新节点。旧节点可能消费PENDING结果却不会创建新的时钟检查点,因此不能与新版混跑。历史结构损坏或缺少完整定义的记录即使形成候选,也必须经运行器校验才能推进。
[存储回归](../internal/storage/mysql/workflow_clock_integration_test.go)覆盖到期、竞争、预算、暂停/满额、回滚、归属损坏与未知结果;[v18升级回归](../internal/storage/mysql/workflow_clock_upgrade_integration_test.go)验证回填及原预算保留;[真实进程回归](../internal/storage/mysql/workflow_runtime_process_integration_test.go)覆盖全部控制进程退出后的定时和查询接续。执行记录见[阶段报告](reports/phase-4-clock.md)。
SIGNAL早到/重复/超期处理及最终查询竞争已接入;内部失败分支结清和控制已接入;下一阶段继续人工恢复、公共控制及实例SDK,完整范围见[持续计划](08-development-plan.md)。
人工恢复跨激活后,查询原截止、连续业务轮编号和同轮请求预算仍保留;首轮已解析输入始终从首次激活读取。具体规则见[有界查询恢复](17-workflow-poll-recovery.md)。