Files
orbit/docs/api/tasks.md

14 KiB
Raw Blame History

任务接口 v1

状态:P1 已实现并通过真实 MySQL 与 SDK 回归。当前提供受理、查询、分页和待执行取消;P2 已提供 Worker 执行与历史查询,P3 已接入 安全重试。

P2 进展:服务端执行内核、WSS 与独立自动 Worker SDK 已可产生 LEASED、RUNNING、SUCCEEDED、FAILED、NEEDS_REVIEW 等状态,见 Worker 契约;尝试与证据查询 可核对每次执行;重试以冻结策略、明确失败分类及未知效果安全声明为依据。

公共约定

业务接口需要 Authorization: Bearer <token>;单项写入需要 Content-Type: application/json 和稳定的 Idempotency-Key;批量受理 在每项正文提供独立幂等键。凭据决定命名空间,请求体不能覆盖它。详见 鉴权与初始化。

请求体上限 512 KiB。拒绝未知字段、大小写别名、重复 JSON 键、无效 UTF-8、孤立 Unicode 代理项和超过 32 层的嵌套。除可空时间外,字段不能显式传 null。请求追踪 ID 由服务端生成,与幂等键分开。

响应使用 {"request_id":"…","data":…};X-Request-ID 与正文一致。revision 是十进制字符串;ID 是 32 位小写十六进制字符串。时间使用 RFC 3339,返回 UTC,最多微秒精度。

POST /v1/tasks

权限:tasks:submit,并有目标队列的访问范围。首次提交返回 201,同键相同内容返回 200。两者均只表示数据库已受理,不表示业务处理完成。

字段 类型 必填 / 默认 含义与限制
queue_id string 必填 同一命名空间内的队列 ID
handler string 必填 处理器名称,1–128 字符
handler_version string 必填 固定处理器版本,1–64 字符
payload_version string 必填 输入契约版本,1–32 字符
payload JSON 必填 非 null JSON,最大 256 KiB;平台不解释业务字段
business_key string 可选 最多 191 个 Unicode 字符,无 NUL/换行;用于关联,不提供业务幂等
concurrency_key string 可选,空为关闭 1–191 个 Unicode 字符,无控制字符和首尾空白;显式资源互斥键,同命名空间跨队列共享,见 资源键契约
priority 整数 可选,默认 0 0–9,冻结优先级,每级30秒排序优势;不提前延时任务,见 优先级与公平派发
available_at time / null 受理时数据库时间 最早可执行时间;必须早于总截止时间,仅允许最多 1 秒的过去时间容差
start_deadline_at time / null 无独立限制 必须晚于当前时间,不早于 available_at,不晚于总截止时间
task_timeout_ms uint64 0 或省略为 86400000 从受理时计算的总时限,最大 30 天(2592000000 毫秒)
recovery_safety string RECONCILE_ONLY 也可为 IDEMPOTENT;必须基于处理器真实的幂等能力声明
retry object 见下表 受理时冻结策略;包含首次领取的有限尝试、退避及每次时限,见 安全重试

处理器及版本名称以 ASCII 字母或数字开头,后续只能包含字母、数字、_ . : -。时间年份为 2000–9998。

retry 字段 默认与限制
max_attempts 0 或省略为 1;范围 1–100,包含首次尝试
attempt_timeout_ms 0 或省略为 30000;必须不超过任务总时限
initial_delay_ms 多次尝试时 0 或省略为 1000;必须为正且不超过 max_delay_ms;单次尝试默认 0
max_delay_ms 多次尝试时 0 或省略为 30000;单次尝试默认 0;最大 9223372036854,整体仍受总截止时间限制

这些值是当前接口保护边界,不代表已经验证相应吞吐或并发容量。

业务 JSON 以经过校验的文本持久化,保留大整数和小数的精确数字内容。不要在接入方先转成 float64;Go 使用 json.RawMessage、结构体中的精确类型或业务约定的字符串。单个数字最长 256 字符,显式指数和去除尾零后的指数均需在 -308 至 308 内,数值不能超出有限 double 的幅度;这只是可接受范围检查,摘要不使用浮点近似值。

请求示例,队列 ID 替换为初始化结果:

curl -i "$ORBIT_URL/v1/tasks" \
  -H "Authorization: Bearer $ORBIT_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: conversion-operation-001' \
  --data '{
    "queue_id":"11111111111111111111111111111111",
    "handler":"file.convert",
    "handler_version":"v1",
    "payload_version":"v1",
    "payload":{"source_ref":"objects/input.txt","format":"pdf","sequence":9007199254740993},
    "business_key":"conversion-001"
  }'

首次受理的完整响应示例,ID 和时间为示意值:

{
  "request_id":"44444444444444444444444444444444",
  "data":{
    "deduplicated":false,
    "task":{
      "id":"22222222222222222222222222222222",
      "namespace_id":"33333333333333333333333333333333",
      "queue_id":"11111111111111111111111111111111",
      "state":"WAITING",
      "revision":"1",
      "handler":"file.convert",
      "handler_version":"v1",
      "payload_version":"v1",
      "payload":{"source_ref":"objects/input.txt","format":"pdf","sequence":9007199254740993},
      "business_key":"conversion-001",
      "available_at":"2026-09-17T00:00:00Z",
      "deadline_at":"2026-09-18T00:00:00Z",
      "recovery_safety":"RECONCILE_ONLY",
      "retry":{"max_attempts":1,"initial_delay_ms":0,"max_delay_ms":0,"attempt_timeout_ms":30000},
      "attempt_count":0,
      "created_at":"2026-09-17T00:00:00Z",
      "updated_at":"2026-09-17T00:00:00Z"
    }
  }
}

可选响应字段:start_deadline_at、result、cancel_requested_at、completed_at、business_key、concurrency_key、retry_of,未设置时省略。effect_uncertain=true 表示存在尚未核实的授权执行效果,false 时省略;详情、摘要和 Worker 派发均可返回。retry_of仅在任务详情、受理回执和Worker派发中标识人工重试的原任务,列表摘要不包含。等待重试时 result/completed_at 省略,历史结果在尝试中保留。终态有 completed_at。状态集合为 WAITING / LEASED / RUNNING / NEEDS_REVIEW / SUCCEEDED / FAILED / CANCELLED;P1 仅主动产生 WAITING 和 CANCELLED。

幂等与失败处理

  • 键为 1–128 个可打印 ASCII 字符,不含空格;作用域为命名空间 + 操作类型,取消额外包含任务 ID。不同凭据在同一命名空间共享该身份,重复请求仍需通过当前权限校验。
  • 固定默认值、规范化 UTC 时间、排序 JSON 对象键、精确规范化十进制数字,再计算版本化 SHA-256 摘要。1 与 1.0 等价;数组顺序有意义。内部算法名 orbit-json-v1,不宣称采用 RFC 8785。
  • 同键不同内容返回 409 IDEMPOTENCY_CONFLICT;不同键即使 business_key 相同,也可以产生两个任务。
  • 同一事务保存任务、受理事件、提交者审计、幂等身份与原始回执;验证或写入失败整体回滚。提交审计记录凭据与处理器/队列,不复制业务参数。
  • 同键重报返回首次操作的回执快照;任务之后取消也不会改写该回执。获取最新状态调用 GET。
  • 网络中断、客户端超时或 503 时,不能认定提交未发生;保持原键和原内容查询或重报。SDK 不自动换键或自动重试写操作。
  • 幂等记录设置 90 天保留截止时间;当前尚无清理任务,存在的记录持续生效。后续清理不得缩短已承诺窗口。

SDK:Client.SubmitTask(ctx, key, request)。

需要将业务变更与提交可靠衔接时,使用事务Outbox接入示例:先在业务事务里冻结原操作和请求,再在事务外调用此接口;新增示例不改变本HTTP契约。

GET /v1/tasks/{task_id}

权限:tasks:read 和任务所在队列范围。200 的 data 直接为上方 task 对象,返回当前状态和完整参数/已保存结果。不存在、跨命名空间或不在该凭据队列范围时返回 404。

curl "$ORBIT_URL/v1/tasks/$TASK_ID" -H "Authorization: Bearer $ORBIT_TOKEN"

SDK:Client.GetTask(ctx, taskID)。

任务直接来源独立通过GET /v1/tasks/{task_id}/source读取,SDK为GetTaskSource(ctx, taskID);可定位普通受理、人工重试父任务、来源计划/触发记录/固定版本。使用相同的tasks:read和队列范围,详情见来源查询。来源未嵌入Task或列表摘要,读取完整计划仍需计划权限。

GET /v1/tasks

权限:tasks:read。查询参数均只能出现一次:

参数 要求
queue_id 必填,有权限的队列 ID
state 可选,取上述状态之一
business_key 可选,精确匹配1–191个Unicode字符,区分尾部空格;显式空值拒绝
created_from / created_before 可选,RFC3339微秒时间;创建时间包含起点、不包含终点,两者同时设置时起点必须更早
limit 可选,默认 20;1–100
cursor 可选,上一页返回值;必须保持队列、状态、业务键和时间范围不变,最长1024字符

完整筛选、时间边界、索引与兼容说明见业务键与时间查询,并提供完整运行示例。

按 (created_at DESC, id DESC) 游标分页,不使用大 offset。不承诺跨页快照:读取过程中状态可能变化。列表省略 payload/result/retry 等大字段,返回摘要。

curl "$ORBIT_URL/v1/tasks?queue_id=$ORBIT_QUEUE_ID&state=WAITING&limit=20" \
  -H "Authorization: Bearer $ORBIT_TOKEN"
{
  "request_id":"44444444444444444444444444444444",
  "data":{
    "items":[{
      "id":"22222222222222222222222222222222",
      "queue_id":"11111111111111111111111111111111",
      "state":"WAITING","revision":"1",
      "handler":"file.convert","handler_version":"v1","business_key":"conversion-001",
      "available_at":"2026-09-17T00:00:00Z","deadline_at":"2026-09-18T00:00:00Z",
      "attempt_count":0,"created_at":"2026-09-17T00:00:00Z","updated_at":"2026-09-17T00:00:00Z"
    }]
  }
}

还有下一页时返回 data.next_cursor。空结果为 items: []。SDK:Client.ListTasks(ctx, request)。

POST /v1/tasks/{task_id}:cancel

权限:tasks:cancel 和队列范围。支持独立任务和计划生成任务的 WAITING、LEASED、RUNNING、NEEDS_REVIEW;流程所属任务仍由后续流程控制管理。运行中只记录取消意图,返回 202,最终状态等待有效结果或超时核查。完整规则及 SDK 示例见 取消契约。

curl "$ORBIT_URL/v1/tasks/$TASK_ID:cancel" \
  -H "Authorization: Bearer $ORBIT_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: conversion-operation-001-cancel' \
  --data '{"expected_revision":"1","reason":"source object was replaced"}'

expected_revision 必填,为非零 uint64 的十进制字符串;reason 必填且不能全为空白,最多 1024 个 Unicode 字符。服务端以版本和状态条件更新,只有一个竞争请求能成功。

200/202 的 data 为 {"task":<本次取消的任务快照>,"deduplicated":false}。上方等待态示例会变为 state=CANCELLED、revision="2",写入 cancel_requested_at/completed_at,并同事务追加取消事件及审计。首次取消之后使用相同键、原因和原 expected_revision重报,返回原取消回执且 deduplicated=true;原回执为 RUNNING 时仍返回 202,最新状态另行 GetTask。换键重报已有取消意图或终态任务返回 409。WAITING/LEASED 若承接历史未知效果,取消转 NEEDS_REVIEW,保留 effect_uncertain=true、completed_at 为空;已在核查的任务保持核查。

SDK:Client.CancelTask(ctx, taskID, key, request)。此操作不承诺回滚任何已发生的外部业务效果。

错误与回归

HTTP / code 处理
400 INVALID_ARGUMENT 修正字段、时间、游标或幂等键
401 UNAUTHENTICATED 检查凭据、到期/撤销和应用/命名空间状态
403 PERMISSION_DENIED 检查动作或提交/列表所需队列范围
404 NOT_FOUND 资源不存在或单资源读取范围不匹配
409 IDEMPOTENCY_CONFLICT 原键已对应其他内容,不应盲目换键重试
409 STATE_CONFLICT 当前状态或版本不允许操作,先查询
413 BODY_TOO_LARGE 减小请求或改为传递业务对象引用
415 UNSUPPORTED_MEDIA_TYPE 使用 application/json
429 APPLICATION_FULL 应用下所有命名空间共享额度已满;按 Retry-After 退避并保留原身份,详见 应用配额
429 QUEUE_FULL 本次未受理,按 Retry-After 有限退避并保留原身份;已受理的原回执不受当前满额限制
503 UNAVAILABLE 结果可能未知;保留原幂等身份,有限退避后核对
{"request_id":"44444444444444444444444444444444","error":{"code":"IDEMPOTENCY_CONFLICT","message":"idempotency key was used with different content"}}

有限自动重试已实现,具体分类、退避、回执和升级边界见 安全重试。运行中取消 已实现;批量受理 已实现逐项结果与原身份补交;独立任务核查与关联新任务重试 已实现。执行历史 已提供尝试与证据的列表和详情。完整可运行程序见 SDK 任务示例,已验证异常场景见 P1 回归报告 及 取消回归。SDK 忽略未知响应字段;更改已有字段语义需升级契约和回归。