Files
orbit/docs/17-workflow-poll-recovery.md

66 lines
6.2 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 有界查询的人工恢复
状态:POLL新激活保留跨激活的轮次、同轮尝试和原绝对截止,附属信号随新激活换代;已通过[公共恢复API](api/workflow-recovery.md)提供独立SDK。PENDING等待接续见[等待恢复](18-workflow-wait-recovery.md),历史实现验证见[报告](reports/phase-5-poll-recovery.md)。完整历史、通用接入场景及生产验证继续交付。
## 1. 业务轮次与请求尝试
查询成功且业务结果为 PENDING 时,后台按原间隔开启下一业务轮。请求失败、未知或结果不符合契约时,进入核查;人工恢复重查同一业务轮,不能通过换激活获得新的请求预算。
例如固定定义最多查询 3 轮,每轮最多尝试 3 次:
| 工作 | 激活 | 业务轮次 | 本任务尝试上限 | 沿用的截止 |
| --- | --- | --- | --- | --- |
| 原查询请求失败,已用 1 次 | 1 | 1 | 3 | 第一轮原任务截止、原查询总截止 |
| 人工恢复同轮 | 2 | 1 | 2 | 同上,人工操作不重新计时 |
| 恢复请求成功返回 PENDING,间隔到达 | 2 | 2 | 3 | 新业务轮的任务截止受原查询总截止截短 |
| 第二轮请求失败,已用 1 次,再人工恢复 | 3 | 2 | 2 | 第二轮第一次任务的绝对截止、原查询总截止 |
恢复前核验真实尝试行数、任务计数、原处理器/版本/策略/输入、业务键、互斥键、所有者、连续激活及 `retry_of` 链。每一业务轮的任务总数也不超过原单轮尝试上限,防止反复取消未开始任务而无限制造激活。一次成功查询已返回 PENDING 的轮次不能人工重试;轮次耗尽须核查最终事实或取消。若 UNKNOWN 经 ReviewTask 被核实为成功 PENDING,须等待结果消费,再通过[ContinuePoll](18-workflow-wait-recovery.md)显式恢复原等待;不能借同轮 RetryStep 绕过间隔或次数。
## 2. 恢复前提和事务
沿用[人工核查约定](16-workflow-recovery.md):显式 `workflows:recover`、最新实例/步骤版本、当前激活、稳定操作键、原因、证据和 `safe_to_retry=true`。只有 NEEDS_REVIEW 实例中待核查、失败或取消的 POLL 可恢复。已有停止意图、实例/查询/原请求截止到达、次数耗尽或历史执行未知时拒绝。
UNKNOWN 必须先核实全部外部执行结束,经 ReviewTask 保存确定结果,并消费其新版本 Inbox 后再恢复。声明不能替代服务端的历史归属、执行、许可和未消费/隔离来源检查。
新激活、新 Pulse 任务、同轮记录、预算、最终信号等待、时钟、额度、审计和幂等回执同事务提交。新任务受当前应用及队列配额约束;满额、唯一性冲突或审计失败全部回滚。相同操作键并发重报只返回一个冻结回执,仍核验当前权限。
恢复后实例仍是 NEEDS_REVIEW,执行门控关闭;继续核查通过后显式 Continue,原 paused 为 true 时还需 RESUME。恢复不会撤销原取消/失败停止目标。
## 3. 数据与历史
[迁移 000022](../internal/storage/mysql/migrations/000022_workflow_poll_recovery.up.sql)为 `PollState` 新增:
| 字段 | 含义 |
| --- | --- |
| first_round | 当前激活保存的首个业务轮次。人工恢复沿用旧激活的最后业务轮编号 |
| first_round_attempts_used | 该轮在此前激活已用的请求尝试数,从原单轮预算扣除 |
`used_rounds` 仍是从首次激活起的业务轮次编号,不是当前激活行数。当前激活轮次必须完整覆盖 `[first_round, used_rounds]`;正常进入下一轮时继续 `used_rounds+1`。查询总截止基于首次激活的创建时间,当前激活创建时间不成为新起点。同轮的新任务以 `retry_of` 关联前一任务,沿用该轮第一次任务的绝对截止。
旧激活状态、输出、任务结果、执行尝试、轮次及信号证据保留;旧查询预算关闭在途/后续检查点并保留原结清时间。来源事件仍固定原激活和任务,旧激活结果只记录 STALE_ACTIVATION,不能完成新任务。停止结清核验所有历史任务,轮次归属按各自的 `first_round` 和 `used_rounds` 检查。
迁移将历史数据补为 `first_round=1`、已用历史尝试为 0,不更改旧轮次、原截止、预算、状态版本及时间。新增两个 Go 字段和 SQL 列均有中文注释。当前共 35 个持久化实体、305 个直接字段声明;无外键、无级联删除。
升级需停止旧写入节点,再执行正式迁移并启动新版。旧节点不了解跨激活轮次与截止传递,不能在新节点创建恢复激活后继续混跑。
## 4. 最终信号的换代
POLL 带最终信号时,恢复创建同契约、同精确关联键、同原截止的新等待,目标激活代次加一。旧已消费信号及旧 winner 保留,不能自动成为新等待赢家。旧代次再次受理的信号保存为 REJECTED / STALE_ACTIVATION;新代次须重新提交有证据的信号。
当前代次仍有 PENDING 信号时,先消费这些事实,恢复拒绝直接丢弃。已有赢家时核对其受理证明与关联后保留历史。最终信号原截止已过时不重新开始等待。
这些规则覆盖 POLL 附属等待;独立 SIGNAL/TIMER 使用[RearmWait](18-workflow-wait-recovery.md),公共接口已接入。
## 5. 内部调用场景
| 场景 | 调用顺序 |
| --- | --- |
| 已知请求失败且仍有预算 | 读取当前实例/步骤 → RetryStep → 核对新任务及剩余预算 → Continue → Worker 执行 → 原查询分类逻辑接续 |
| 请求 UNKNOWN | 核实外部执行结束 → ReviewTask → 等待 Inbox 消费 → 读取当前版本 → RetryStep → Continue |
| 恢复后收到旧结果 | 内部 Inbox 消费只保存旧激活处置,不推进当前激活 |
| 恢复后收到新最终回执 | 用新激活代次发送同契约信号 → 按原受理时间裁决并结清当前查询 |
| 原轮次/请求预算或截止耗尽 | 拒绝新查询;按证据 ResolveStep 或继续取消/停止结清 |
实现入口是[内部恢复服务](../weave/recovery/service.go)和[POLL 存储事务](../internal/storage/mysql/workflow_poll_retry.go)。这些场景尚不是公共 SDK 用法;公共接口、可运行示例和生产拓扑验收仍须交付。