Files
orbit/docs/14-workflow-signals.md

69 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.
# Weave 外部信号与最终查询结果
状态:信号受理/查询、SIGNAL等待、早到保存、截止处理及POLL最终信号竞争已接入MySQL和后台运行器,并已提供[公共HTTP与独立SDK](api/workflows.md#5-有身份的外部信号)。控制和人工恢复也已接入;完整历史与更多场景继续按[计划](08-development-plan.md)推进。
## 1. 受理身份与权限
[内部契约](../weave/signal/service.go)为 `Send(token, workflowID, idempotencyKey, input)` 与 `Get(token, workflowID, signalID)`。输入绑定固定定义中的信号名称、内容版本、步骤键、显式目标激活序号和精确操作关联,附内容、受理原因与可选证据引用。
| 输入 | 限制及语义 |
| --- | --- |
| name / payload_version | 定义中冻结的名称/内容契约;ASCII,最多128/32字符 |
| target_step_key | 固定定义的目标步骤,最多64字符,必须为SIGNAL或带最终信号的POLL |
| target_activation_sequence | 显式正数;首次和早到信号为1,旧信号不能跟随人工恢复后的新激活;尚不存在的未来代次拒绝 |
| correlation_key | 1至512字节UTF8,精确匹配,保留尾部空格;不由调用者随意省略 |
| payload | 固定类型的精确JSON,最多256KiB;显式null与缺失不同;POLL最终信号不能是PENDING |
| reason / evidence_ref | 必填原因最多1024字符;可选证据引用最多512字符、不含空白/控制符;平台不访问引用地址 |
提交要求显式 `workflows:signal` 权限;查询要求 `workflows:read`。两者都核对当前凭据、命名空间、实例固定版本及全部相关队列权限。actor来自认证凭据,不能请求自报。现有bootstrap的定义发布授权不会自动授予信号操作权限。
提交按认证 → 应用额度 → 原幂等身份 → 排序队列 → 实例/门控/步骤持锁。首次受理将信号、检查点、事件、审计和冻结回执同事务写入。审计包含原请求摘要、受理时间和总截止;运行器会核对受理证明,参数被替换时转核查,数据库连接/SQL错误则保留原来源并退避重试。
同一实例最多保存1024条信号,包含拒绝及终结历史;原幂等身份恢复先于上限检查。同键同内容返回首次冻结回执,同键不同内容冲突。重报仍校验当前权限。受理回执中的PENDING不跟随消费变化,当前状态用Get读取。
## 2. 早到、迟到与消费
目标步骤尚未激活时,信号保持PENDING;步骤激活会创建立即到期的检查点扫描早到信号。每个等待固定名称、版本、操作关联、激活序号和原截止,消费选择按受理时间、信号ID排序的第一条匹配记录。
- 信号必须在等待截止之前受理,等于截止不算按时。按时受理但控制进程稍后恢复,可以继续收集该事实。
- 暂停或应用停用仍可收集结果;后继工作受原门控、额度及实例总截止约束。
- 已激活时的关联不符、旧代次、已结清步骤或实例会保存明确拒绝原因;内容类型/定义契约无效的请求直接拒绝,不产生有效信号。
- 同一等待只记录一个winner_signal_id;其余同代次信号保存WAIT_SETTLED或QUERY_SETTLED等原因。所有关联由事务核验,不使用外键。
- SIGNAL在没有按时匹配信号的情况下到期,将等待标为EXPIRED、步骤和实例转NEEDS_REVIEW,并关闭新执行门控。
信号的expires_at固定为首次受理时的实例总截止。尚未匹配的早到信号通过单独到期索引结清;available_at仅表示下次检查时间,故障退避不能改变expires_at、accepted_at或等待截止。若实例已过总截止且仍需新工作,保留已收到事实并进入核查。
消费确认、等待赢家、步骤结果、后继任务或根检查点及审计同事务。审计失败不会留下“信号已消费但后继未保存”的部分状态。
## 3. 最终信号与POLL查询竞争
查询请求由Pulse执行,最终信号和查询结果由同一个实例事务裁决。PENDING只是一次成功查询观察,不争夺最终结果;请求失败也不允许创建新业务轮次绕过Pulse尝试预算。
| 条件 | 处理 |
| --- | --- |
| 最终查询结果早于信号受理,且在原查询截止内 | 查询结果胜出,信号保存QUERY_SETTLED;精度相同的时间优先查询 |
| 已有最终信号,当前无在途查询 | 结清POLL,不再创建下一轮查询 |
| 最终信号先到,查询尚未开始 | 保存信号赢家,使用Pulse取消规则结清未开始任务,再收集其Inbox结果 |
| 最终信号先到,查询正在执行 | 保存赢家和取消意图;保留当前任务、ACTIVE步骤及执行许可,等待Pulse实际结果 |
| 在途查询随后返回PENDING、相同最终结果或已知失败/取消 | 收集该查询结清后采用信号结果,不新增查询轮次 |
| 在途查询效果未知、结果不合法或返回不同最终结果 | 保留信号和查询证据,转NEEDS_REVIEW;不能让信号覆盖未知事实 |
| 原查询总截止已到,查询仍在途 | 关闭新执行门控,保留当前任务等待结清;后续结果不能自动清除已建立的核查状态 |
`CONSUMED`表示信号已被等待采用,不表示整个实例成功。带在途查询时,即使等待记录为SATISFIED,步骤仍保持ACTIVE直到Pulse确认结束。取消复用Pulse既有许可释放逻辑,不在Weave中实现另一套释放计数。
控制时钟发现查询结果已落库但未消费时,读取原Inbox事件参与裁决;不由扫描器先后决定赢家,也不绕过Inbox故障退避。结果、信号和许可预算均在MySQL持久保存,进程重启不重新选择已保存的赢家。
## 4. 存储升级和运行边界
[迁移000020](../internal/storage/mysql/migrations/000020_workflow_signal_identity.up.sql)增加信号契约/代次/证据/处置/到期检查时间,以及等待的赢家引用。名称扩至128字符,关联保持精确字节,并兼容历史764字节值。截至000020为35个实体、301个直接持久化字段声明,均提供中文说明;数据库无外键、无级联删除。
历史信号没有足够身份和受理证明:旧PENDING标记REJECTED/LEGACY_UNVERIFIED,保留原载荷和时间;已消费历史不被重新解释。旧NULL等待元数据不能推测成有效新契约。迁移为当前SIGNAL等待回填原截止的检查点,保留已有检查点,排除终态实例及旧激活。
升级仍采用停止旧写入节点 → 应用全部迁移 → 启动新版;新版信号字段和运行语义不支持与旧节点混跑。后台扫描三类候选:结果Inbox、根/等待检查点和未匹配信号到期;每类最多100项,最终批次最多100项,每个候选短事务处理。
## 5. 验证与后续
[受理回归](../internal/storage/mysql/workflow_signals_integration_test.go)、[消费回归](../internal/storage/mysql/workflow_signal_runtime_integration_test.go)、[真实v19升级](../internal/storage/mysql/workflow_signal_upgrade_integration_test.go)和[实际进程恢复](../internal/storage/mysql/workflow_runtime_process_integration_test.go)对应受理/并发/故障/升级边界,实际运行结果记录在[阶段报告](reports/phase-4-signals.md)。
内部失败分支取消结清、实例暂停/继续/取消已接入。人工核查及[POLL新激活的最终信号换代](17-workflow-poll-recovery.md)已接入;[PENDING接续与独立等待重建](18-workflow-wait-recovery.md)已接入内部服务,实例与信号HTTP、独立SDK和基础场景已接入,完整历史及更多场景继续交付。公共接入验收另见[报告](reports/phase-5-public-api.md),也不替代生产MySQL切换与容量测试。