Files
orbit/docs/api/workflow-recovery.md

9.0 KiB
Raw Permalink Blame History

流程人工核查与恢复 API / SDK

状态:六类内部恢复已接入公共HTTP和独立SDK。所有路径以 /v1/workflows/{id}/recovery/ 开头,均为POST,要求 workflows:recover、全部固定队列范围、Bearer凭据、稳定Idempotency-Key及application/json。成功首次与重复响应均为200。身份、读查询、错误码见实例接口。

恢复只针对NEEDS_REVIEW实例。SDK不会自动重试、补齐声明、刷新版本或生成新键。核查前应读取当前实例、当前步骤、相关任务及外部证据;每次新决定使用当前版本,响应未知则用原决定原键核对。输入和结果最多256 KiB,请求体总计最多512 KiB。

1. 公共字段

所有请求包含WorkflowRecoveryDecision的扁平字段:

字段 必填与语义
expected_revision 必填,当前实例的非零revision十进制字符串,不能用control_revision替代
reason 必填,1–1024个Unicode字符;不允许全空白或NUL
evidence_ref 必填,1–512个Unicode字符,不含空白或控制字符;平台不访问引用、不存放秘密

步骤类请求还包含WorkflowStepDecision的扁平字段:step_key(固定定义步骤名,最多64个ASCII字符)、activation_id(当前32位激活ID)、expected_step_revision(当前非零步骤版本字符串)。实例版本、步骤版本及激活身份一起检查,旧激活不能操作新工作。

服务端检查待消费/隔离Inbox、历史任务归属、仍在执行的尝试及未释放许可。声明为true是操作者确认,不替代服务端验证。恢复不会重置原绝对截止、次数或业务关联;已有停止目标不会被撤销。

2. 六类命令

路径后缀 SDK方法 / 请求类型 额外必填字段与结果
task-review ReviewWorkflowTask / ReviewWorkflowTaskRequest task_id、expected_task_revision字符串、execution_settled=true、outcome=SUCCEEDED或FAILED、非null result;核实本实例任一历史激活的待核查执行
step-resolve ResolveWorkflowStep / ResolveWorkflowStepRequest 步骤公共字段、execution_settled=true、outcome=SUCCEEDED或FAILED、result;确认当前叶子步骤的逻辑结论
continue ContinueWorkflow / ContinueWorkflowRequest review_resolved=true;确认全部核查事项已处理后解除核查
step-retry RetryWorkflowStep / RetryWorkflowStepRequest 步骤公共字段、safe_to_retry=true;为TASK/POLL创建新激活,保留原预算和截止
poll-continue ContinueWorkflowPoll / ContinueWorkflowPollRequest 步骤公共字段、pending_confirmed=true;权威结果已确认为PENDING时按原间隔恢复等待,不重查同轮
wait-rearm RearmWorkflowWait / RearmWorkflowWaitRequest 步骤公共字段、wait_verified=true;独立SIGNAL/TIMER在原截止内更换等待代次,含首次最多32代

所有确认布尔值必须在HTTP中显式提供且为true。拒绝嵌套Decision、伪造actor_id、大小写别名和未声明字段。

执行核查请求示例:

{
  "expected_revision":"8",
  "reason":"已核实请求完成且外部作业仍在处理",
  "evidence_ref":"evidence:query-001",
  "task_id":"88888888888888888888888888888888",
  "expected_task_revision":"4",
  "execution_settled":true,
  "outcome":"SUCCEEDED",
  "result":"PENDING"
}

ReviewTask只改变执行结论,旧UNKNOWN尝试和证据保持原样。它产生新的内部结果,必须等待后台消费,当前步骤及实例不会自动解除核查。POLL请求成功不等于流程业务成功;ResolveStep对POLL的成功结果必须映射到固定的业务成功值,不能把PENDING写成步骤成功。TIMER成功只能在首次等待时长满足后确认,result必须为显式null。

PENDING接续请求示例:

{
  "expected_revision":"11",
  "reason":"沿原间隔继续查询外部作业",
  "evidence_ref":"evidence:query-001",
  "step_key":"query",
  "activation_id":"66666666666666666666666666666666",
  "expected_step_revision":"3",
  "pending_confirmed":true
}

相同字段布局适用于step-resolve、step-retry和wait-rearm,额外字段按上表替换。Continue只有实例公共字段及review_resolved,不要求步骤字段。

3. 回执与当前视图

六个方法统一返回WorkflowRecoveryResult;HTTP的data字段为该对象:

字段 含义
instance 首次管理事务完成时的完整实例快照,字段同启动回执
audit_id 本次流程管理审计ID,可用GetWorkflowOperation读取原因、证据及前后摘要
step 步骤类操作返回当前步骤快照,包含激活代次;不是后续实时状态
task task-review或step-retry返回执行任务快照;字段同GetTask
task_audit_id task-review对应的独立任务审计ID,与流程审计同事务保存
deduplicated 是否恢复原回执;恢复原键仍检查当前权限

步骤恢复回执示意,省略字段按协议确实不存在,并非JSON占位:

{
  "request_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "data":{
    "instance":{
      "id":"22222222222222222222222222222222",
      "namespace_id":"33333333333333333333333333333333",
      "definition_id":"11111111111111111111111111111111",
      "definition_version_id":"44444444444444444444444444444444",
      "version":"v1","state":"NEEDS_REVIEW","revision":"13",
      "paused":false,"control_revision":"3",
      "input":{"n":9007199254740993},"business_key":"operation-001 ",
      "deadline_at":"2026-09-18T08:01:00Z",
      "created_at":"2026-09-18T08:00:00Z","updated_at":"2026-09-18T08:00:10Z"
    },
    "audit_id":"99999999999999999999999999999999",
    "step":{
      "id":"55555555555555555555555555555555","key":"query","kind":"POLL",
      "state":"WAITING","revision":"4",
      "activation_id":"66666666666666666666666666666666","activation_sequence":1
    },
    "deduplicated":false
  }
}

ContinuePoll保留当前激活与信号代次;RetryStep及RearmWait创建新激活。恢复等待或创建新任务后仍须显式Continue解除核查;paused仍为true时还需ControlWorkflow(RESUME)。任何恢复操作都不盲目撤回已发生的外部业务效果。

4. SDK接入顺序

以下为嵌入接入方函数的调用片段,api是已认证Client,workflowID为目标实例,operationKey与本次完整命令需要由调用方持久保存:

current, err := api.GetWorkflowStep(ctx, workflowID, "query")
if err != nil { return err }
if current.Step.ActivationID == nil { return errors.New("query is not activated") }
command := orbit.ContinueWorkflowPollRequest{
    WorkflowStepDecision: orbit.WorkflowStepDecision{
        WorkflowRecoveryDecision: orbit.WorkflowRecoveryDecision{
            ExpectedRevision: current.Revision,
            Reason: "已核实权威PENDING结果,沿原预算接续",
            EvidenceRef: "evidence:query-001",
        },
        StepKey: current.Step.Key,
        ActivationID: *current.Step.ActivationID,
        ExpectedStepRevision: current.Step.Revision,
    },
    PendingConfirmed: true,
}
receipt, err := api.ContinueWorkflowPoll(ctx, workflowID, operationKey, command)
if err != nil { return err } // 不生成新键绕过冲突;结果不明时原命令原键核对。
audit, err := api.GetWorkflowOperation(ctx, workflowID, receipt.AuditID)
if err != nil { return err }
fmt.Println(audit.Action, receipt.Instance.State)

完整、可执行的六类SDK场景位于真实服务集成测试:UNKNOWN→执行核查→逻辑结论→解除核查;已知失败→剩余预算重试,同时重建信号等待;UNKNOWN查询→核查为PENDING→原间隔接续。运行方式为仓库根目录make integration,由脚本创建隔离MySQL、实际服务及凭据。

一般步骤是:查询当前事实 → 提交一个有证据的决定 → 等待该决定产生的内部结果消费 → 重新读取当前版本 → 再提交下一决定。不要自动将任意STATE_CONFLICT重试为“最新版本”;冲突可能意味着新的在途结果、权限变化、原截止已到或预算耗尽,应重新核查。相同命令的响应未知则仍使用原版本和原键。

5. 兼容与剩余范围

新路由仅在本开发版本及之后可用;旧服务返回404时不能降级为普通任务重试来绕过Weave所有权。SDK类型独立、版本字段保留uint64精度。规则及数据模型未增加表、字段、外键或级联。

历史激活/轮次/信号及审计、实例筛选、状态时间线和任务历史激活来源均已接入。独立提交后断线与人工恢复示例提供完整启动、故障注入、三步核查、暂停门控与审计核对。生产数据库切换、容量及恢复目标继续按P6验证,原范围剩余故障见P4/P5核对。