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

9.7 KiB
Raw Blame History

流程状态事件时间线

HTTP 与独立 Go SDK 已接入。用于查看流程、用户定义步骤与信号已经保存的状态变化;原事件不会被后续恢复覆盖。运行方法见并行作业与回执示例,验证记录见时间线报告。

1. 路由、权限与范围

方法与路径 权限 Go SDK
GET /v1/workflows/{id}/events workflows:audit:read ListWorkflowEvents(ctx, id, page)
GET /v1/workflows/{id}/events/{event} workflows:audit:read GetWorkflowEvent(ctx, id, event)

成功为200,使用 Authorization: Bearer <token>,无需请求体或幂等键。id和event均为32位小写十六进制。两条路由都要求显式审计权限,普通workflows:read不包含此权限;开发初始化用 --workflow-audit-access。每次读取都核验当前命名空间及实例固定版本引用的全部队列,包括尚未执行的分支。

数据库派生的workflow_id只用于缩小查询范围,不能授予权限。服务端继续核对真实实例、步骤、信号及事件引用的历史激活归属;内部$root步骤不出现在公开时间线。详情必须同时指定父实例和事件ID,不能用全局事件ID越权。

本接口提供逻辑状态事件。任务执行次数、原输入结果、POLL业务轮次、等待预算和人工决定通过下列接口关联查看:

要查看的内容 接口
当前步骤、任务引用、等待原因和剩余预算 实例与步骤详情
指定历史激活的输入输出、等待、POLL轮次 流程历史
Pulse尝试与迟到/矛盾证据 执行历史
管理操作人、原因、证据及前后摘要 流程操作审计、信号审计
任务的固定版本、步骤和历史激活来源 任务来源

事件元数据只保留当时实际写入的引用,例如activation_id或audit_id;旧事件没有的字段不会用当前资源补造。操作审计与Pulse尝试不复制进本接口,避免将不同记录误认为同一种状态转换。

2. 摘要分页

只接受可选limit和cursor。limit为1–100,省略默认20;SDK的0表示省略。cursor最多1024字节,需进行URL编码。未知、重复、非法参数返回400;详情不接受查询参数。

curl --get "$ORBIT_URL/v1/workflows/$WORKFLOW_ID/events" \
  --header "Authorization: Bearer $ORBIT_TOKEN" \
  --data-urlencode 'limit=20'

响应示例:

{
  "request_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "data": {
    "workflow_id": "11111111111111111111111111111111",
    "revision": "12",
    "items": [{
      "id": "22222222222222222222222222222222",
      "workflow_id": "11111111111111111111111111111111",
      "aggregate_kind": "workflow_signal",
      "aggregate_id": "33333333333333333333333333333333",
      "aggregate_revision": "2",
      "type": "workflow.signal.changed",
      "state_scope": "SIGNAL",
      "state": "CONSUMED",
      "occurred_at": "2026-09-18T08:00:00.123456Z"
    }]
  }
}
字段 含义
workflow_id 本页授权的实例
revision 读取本页时的实例版本,uint64字符串;不是事件消费水位
items 有界摘要;空列表为[],不带payload或业务输入结果
next_cursor 还有更旧记录时返回;没有下一页则省略
items[].id 原不可变事件ID
aggregate_kind / aggregate_id 来源类型及身份:workflow、workflow_step、workflow_signal
aggregate_revision 来源资源当时的版本,uint64字符串,各资源独立计数
type 当时保存的事件类型
state_scope / state WORKFLOW、STEP或SIGNAL,以及该对象当时保存的状态
step_key 仅步骤事件返回,取自固定定义,不返回内部根步骤
occurred_at 数据库记录事件的UTC微秒时间,RFC3339;不是外部业务发生时间

按occurred_at、id双键倒序,相同微秒由ID稳定定位。游标绑定命名空间、实例和事件查询种类,不能用于其他实例、操作审计或激活列表。跨页不承诺固定快照,新提交或迟到提交的事件可能落在已经翻过的位置;重新从第一页读取可查看更新。

时间戳及ID均不能表达严格因果或提交顺序。例如同一个启动事务中,首步状态可能先于workflow.started写入;跨资源aggregate_revision不能比较大小。定时退避可能改变资源版本而不改变状态,因此状态事件的版本也可能不连续。需要可靠处理Pulse结果时使用持久订阅,不要将此审计分页当作消息消费协议。

3. 类型与原事件详情

type aggregate_kind state_scope 记录内容
workflow.started workflow WORKFLOW 首次启动、固定定义及版本
workflow.advanced workflow WORKFLOW 持久推进时保存的实例状态
workflow.controlled workflow WORKFLOW 暂停、继续、取消的控制结果
workflow.recovered workflow WORKFLOW 人工恢复决定及当时关联
workflow.blocked workflow WORKFLOW 进入核查等阻塞状态
workflow.stopped workflow WORKFLOW 停止意图结清后的终态
workflow.step.changed workflow_step STEP 步骤当时状态和已记录的激活/原因
workflow.signal.accepted workflow SIGNAL 保存信号受理记录,state可为PENDING、REJECTED或EXPIRED
workflow.signal.changed workflow_signal SIGNAL 已有PENDING信号被消费、拒绝或过期

workflow.signal.accepted沿用历史格式,aggregate_id和aggregate_revision属于实例;state_scope为SIGNAL,信号ID在payload.signal_id中。受理成功不等于信号被消费,受理时就被拒绝的记录不会再制造一个虚假的PENDING转换。

详情data在摘要字段外增加payload,为原事件元数据对象,最大64KiB。例如上述信号消费事件的payload:

{
  "workflow_id": "11111111111111111111111111111111",
  "signal_id": "33333333333333333333333333333333",
  "from": "PENDING",
  "state": "CONSUMED",
  "disposition": "WAIT_MATCHED",
  "consumed_wait_id": "44444444444444444444444444444444"
}
curl "$ORBIT_URL/v1/workflows/$WORKFLOW_ID/events/$EVENT_ID" \
  --header "Authorization: Bearer $ORBIT_TOKEN"

信号消费、过期和拒绝,与对应事件在同一数据库事务提交。批量拒绝最多处理单实例既有上限1024条,每32条批量追加事件;任一批失败会回滚整笔转换,重复处理不重复制造事件。该原子性覆盖平台记录,不保证外部系统业务副作用只发生一次。

4. SDK调用

类型见workflow_events.go。返回revision和aggregate_revision自动解析为uint64;Payload为json.RawMessage,保留精确数字,进一步解析未知元数据时用json.Decoder.UseNumber或明确整数类型。

cursor := ""
for {
    page, err := client.ListWorkflowEvents(ctx, workflowID,
        orbit.HistoryPageRequest{Limit: 20, Cursor: cursor})
    if err != nil { return err }
    for _, event := range page.Items {
        fmt.Printf("%s %s %s %s\n", event.OccurredAt, event.Type,
            event.StateScope, event.State)
        // 仅在需要展开某条事件时读取原元数据。
        if event.Type == "workflow.signal.changed" {
            detail, err := client.GetWorkflowEvent(ctx, workflowID, event.ID)
            if err != nil { return err }
            fmt.Printf("%s\n", detail.Payload)
        }
    }
    if page.NextCursor == "" { break }
    cursor = page.NextCursor
}

完整workflow示例运行真实Worker,保存外部回执后按每页5条读取时间线,核对CONSUMED事件的原PENDING状态及赢家等待ID。

5. 错误、升级与验证边界

HTTP / code 处理
400 / INVALID_ARGUMENT 修正非法ID、limit、cursor或参数;不要删除父资源或切换查询种类重试
401 / UNAUTHENTICATED 检查有效凭据
403 / PERMISSION_DENIED 显式授权workflows:audit:read
404 / NOT_FOUND 实例/事件不存在、父子错配、范围不可访问;不按全局ID降级查找
503 / UNAVAILABLE 数据库故障、已选事件归属损坏、未知历史类型或超大元数据;保留现场核查,不能把空页当作成功

只读请求可在原实例与游标下有限重试;数据改变后结果可能不同。取消HTTP只取消本次读取,不控制流程。SDK拒绝父资源不一致、无效摘要、排序不符和超限详情,不自动补造历史。

迁移000025新增只读派生列及(namespace_id,workflow_id,occurred_at,id)索引,无外键和级联。已有事件原字段不变;无合法流程范围的历史行派生为NULL,不进入流程时间线。新读取仍核验实际归属,但本接口不是全库损坏扫描,无法自动找出被错误范围隐藏的历史行。

旧版本没有保存的信号转换不会回填。部署时先停止旧写入节点,完成迁移再启动新节点,否则混合写入期间可能继续缺少转换事件。STORED生成列和索引需要读取历史数据并可能重建表,生产升级须预演锁等待、时长及磁盘空间;不承诺无停机DDL。旧不可变迁移保持原校验和。

真实MySQL验证包括升级原文保留、零外键、相同时间分页、授权、跨父资源、消费/拒绝/过期、分批写入失败回滚、重复处理和历史激活恢复;HTTP/SDK及完整例子已接入标准回归。大库索引开销、生产故障域、主从切换与全量运维仍属于P6/P7验收。