Files
orbit/docs/api/queues.md

152 lines
9.9 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.
# 队列接口 v1
状态:已实现创建、查询、配置替换、暂停/恢复、共享并发、开始速率与未终结任务背压。MySQL 是跨节点额度的权威存储;没有额外缓存或消息队列依赖。
## POST /v1/queues
需要 queues:manage 和当前命名空间内全部队列范围。鉴权、请求体、幂等键及错误信封与 [任务接口](tasks.md) 一致。
| 字段 | 类型 | 要求 |
| --- | --- | --- |
| name | string | 1–128 字符,以 ASCII 字母/数字开头,其后允许字母、数字、`_ . : -`;命名空间内唯一 |
| max_concurrency | uint32 | 必填,1–100000 |
| rate_per_second | uint32 | 必填,1–1000000 |
| max_unfinished_tasks | uint32 | 可选,省略/零为 100000;有效范围 1–1000000,包含待核查任务 |
```sh
curl "$ORBIT_URL/v1/queues" \
-H "Authorization: Bearer $ORBIT_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: queue-images-v1' \
--data '{"name":"images","max_concurrency":8,"rate_per_second":100}'
```
首次 201:
```json
{
"request_id":"44444444444444444444444444444444",
"data":{
"deduplicated":false,
"queue":{
"id":"11111111111111111111111111111111",
"namespace_id":"33333333333333333333333333333333",
"name":"images","paused":false,"max_concurrency":8,"rate_per_second":100,
"max_unfinished_tasks":100000,"unfinished_tasks":"0","revision":"1"
}
}
}
```
同键同内容返回 200、deduplicated=true。同键不同内容返回 409 IDEMPOTENCY_CONFLICT;换键创建同名队列返回 409 STATE_CONFLICT。创建、幂等记录和管理审计同事务提交。
SDK:`Client.CreateQueue(ctx, key, orbit.CreateQueueRequest{...})`。
## GET /v1/queues/{queue_id}
权限:queues:read 和对应队列范围。200 的 data 直接为上方 queue 对象;资源不存在或单资源范围不匹配返回 404。
```sh
curl "$ORBIT_URL/v1/queues/$ORBIT_QUEUE_ID" -H "Authorization: Bearer $ORBIT_TOKEN"
```
SDK:`Client.GetQueue(ctx, queueID)`。范围限制、重复创建和同名冲突已纳入 [P1 回归](../reports/phase-1.md)。这些配置上限不是生产容量承诺。
unfinished_tasks 为 uint64 的十进制字符串,表示当前 WAITING、LEASED、RUNNING、NEEDS_REVIEW 的总数。它是运行计数,变化不增加队列配置 revision。创建/配置回执保存操作时的快照,获取实时积压需要再次 GetQueue。
## POST /v1/queues/{queue_id}:configure
需要 queues:manage 及对应队列范围。完整替换控制参数,不改变名称、队列身份、已受理任务的冻结策略或历史结果。
| 字段 | 类型 | 要求 |
| --- | --- | --- |
| expected_revision | string | 必填,非零 uint64 的十进制字符串 |
| reason | string | 必填,非空白,最多 1024 个 Unicode 字符,写入审计 |
| paused | bool | 必须显式提供;省略/null 都拒绝 |
| max_concurrency | uint32 | 必填,1–100000 |
| rate_per_second | uint32 | 必填,1–1000000 |
| max_unfinished_tasks | uint32 | 必填,1–1000000;配置命令不使用零值默认 |
```sh
curl "$ORBIT_URL/v1/queues/$ORBIT_QUEUE_ID:configure" \
-H "Authorization: Bearer $ORBIT_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: queue-control-001' \
--data '{"expected_revision":"1","reason":"limit background traffic","paused":false,"max_concurrency":2,"rate_per_second":1,"max_unfinished_tasks":100}'
```
成功 200 的 data 形状与创建一致,队列配置 revision 加一。配置、并发限制同步、审计、事件和幂等回执在一个事务提交。原键、原 expected_revision 和原内容重报返回原快照,deduplicated=true;同键不同内容为 409 IDEMPOTENCY_CONFLICT,新键使用旧版本为 409 STATE_CONFLICT。权限每次重新检查。
SDK:`Client.ConfigureQueue(ctx, queueID, key, orbit.ConfigureQueueRequest{...})` 返回 CreateQueueResult。先 GetQueue 读取版本,网络结果不明时保留原命令核对;只有确认版本冲突并重新评估后,才建立新的配置操作。
## 并发、限速与背压的区别
| 控制 | 实际行为 |
| --- | --- |
| paused=true | 阻止新的领取和 Start;继续受理任务、计划生成、续租、结果确认、取消及恢复 |
| max_concurrency | 限制队列有效执行许可,包括 LEASED 和 RUNNING;许可在结果/取消/恢复事务中释放 |
| rate_per_second | 每个数据库一秒计数窗口内最多提交的 Start 授权数;续租和原回执重报不计数 |
| max_unfinished_tasks | 限制新任务进入队列;等待、退避、预占、运行、待核查都占一个名额 |
速率窗口由第一次成功 Start 建立,保存到 MySQL;所有节点共享。窗口结束后的下一次 Start 建立新窗口。一次窗口内可突发至设定值,窗口交界附近可能出现接近两倍的短时突发;这不是“任意滑动一秒”的承诺,也不限制业务处理器内部发出请求的速率。开始授权成功不证明外部业务效果已经发生。
领取还预留尚未开始尝试所需的窗口空间,额度不足时保持 WAITING,不创建额外尝试。若领取后管理员降低速率,Start 可返回 WS RATE_LIMITED;不批准开始、不写回执、不增加尝试次数。自动 SDK 在同一租约内按原消息身份退避核对,底层客户端也必须遵守剩余租约,不能绕过开始授权执行。限流不延长总截止、开始截止或尝试预算。
降并发不撤销已有许可,超出新上限时等待在途工作收尾再领取;降速率保留当前窗口已用次数;降积压上限保留已有任务,禁止新受理直到低于新上限。调整参数、暂停/恢复、断开连接和节点重启都不能清零使用量。
任务第一次进入 SUCCEEDED、FAILED 或 CANCELLED 时,积压额度同事务减一;重试和待核查不释放、不重复增加。运行中取消只保存意图,仍占名额。迟到证据及重复确认不改变计数。人工核查解除入口仍待 P5,不能为了释放额度把未知结果改成成功/取消。
### HTTP 429 QUEUE_FULL
满额时新的 Submit 返回 429,`Retry-After: 1`,错误信封为:
```json
{"request_id":"44444444444444444444444444444444","error":{"code":"QUEUE_FULL","message":"queue unfinished task limit reached; retry later using the original identity"}}
```
该请求没有受理任务,新增占位幂等记录也回滚。已受理请求的同身份重报优先返回原回执,不被当前满额拒绝。释放空间后使用被拒请求的原键、原内容有限重试;建议至少等待 Retry-After 并加入抖动/总截止。若绝对 available_at/start_deadline_at 已过期,原请求会被参数规则拒绝,不能无限重试。
SDK 返回 `*orbit.APIError`,Code=QUEUE_FULL、StatusCode=429、RetryAfter=time.Second。RetryAfter 解析服务端整数秒建议,缺失/无效/超过一小时为零;SDK 不自动重报业务写请求。取消、查询、结果上报和管理配置不受受理满额限制。
计划触发也使用相同名额。部分补跑已生成的任务正常提交;满额的原触发点保留为下一游标,不创建假任务或跳过记录。下一批继续使用冻结的漏跑策略,因此等待期间超过补偿窗口的点仍可能按策略记录 SKIPPED。队列暂停仅控制执行,要停止计划生成应另外暂停计划。
## 完整 SDK 场景
源码:[resources/main.go](../../examples/resources/main.go)。先完成当前迁移并启动服务,初始化带 Worker 权限且 queues:manage 范围为全部队列的专用开发凭据:
```sh
mkdir -p .local
chmod 700 .local
go run ./cmd/orbit-admin bootstrap \
--application resources-demo --namespace default --environment development \
--queue bootstrap --worker-access --output .local/resources.credentials.json
cd examples
ORBIT_URL=http://127.0.0.1:8080 \
ORBIT_CREDENTIALS_FILE=../.local/resources.credentials.json \
go run ./resources
```
示例创建独立队列(积压 2、并发 2、每秒 1 次开始),暂停后提交两个任务;第三个被拒,重复第一个仍得到原回执;取消第一个释放空间,用第三个原身份重新提交,然后恢复并执行两个纯数据回显任务。
```text
backpressure=QUEUE_FULL http=429
duplicate=true rejected_request_retried=true
task=<第二个任务ID> state=SUCCEEDED attempts=1
task=<第三个任务ID> state=SUCCEEDED attempts=1
unfinished=0 rate_per_second=1 worker_drained=true
```
每次运行创建独立队列和任务,历史保留,不硬删除。无外部业务副作用;任务自带 20 秒总截止,失败非零退出并有界收尾。示例遇到未知网络结果会退出,不自动换键重建资源。
## 升级与未完成范围
000009 增加四个带中文说明的队列字段,回填所有既有未终结任务;旧配置版本及任务内容保持不变。先停止旧服务和写入,运行迁移,再启动新版。只读检查不能代替升级维护窗口;大表回填及混合版本升级待生产环境验证。
创建命令省略新字段时,幂等摘要保留旧字段集合。旧创建回执不含新字段,SDK 解码为零,只表示历史快照缺少信息,实时配置另行 GetQueue。配置接口是新能力;自动 SDK 已能处理 RATE_LIMITED,进行降额管理前应先升级 Worker。
本阶段验证见 [资源控制报告](../reports/phase-3-queue-resources.md)。[应用级跨命名空间配额](application-resources.md) 已实现并同时生效。[资源键许可](keyed-concurrency.md) 与 [优先级/节点公平派发](priority-and-fairness.md) 已实现;可靠事件积压限制及跨节点公平负载和容量验收继续推进。
## 永久退役
`POST /v1/queues/{id}:retire`,需要 `queues:manage`、原幂等键、`expected_revision` 和 `reason`。完整前置条件、错误和 SDK 用法见[通用资源退役](resource-retirement.md)。返回原 QueueResult,新增可空 `queue.retired_at`;暂停仍允许受理,退役禁止新工作。历史读取保持可用。