feat: add precise task business key and creation time queries

This commit is contained in:
2026-09-17 22:19:36 +08:00
parent a50d5faac7
commit 7641423f9d
32 changed files with 1105 additions and 93 deletions

117
docs/api/task-queries.md Normal file
View File

@@ -0,0 +1,117 @@
# 按业务键与创建时间查询任务
状态:P3 已实现HTTP、Go SDK及[运行示例](../examples/task-query.md)。这是 `GET /v1/tasks` 的扩展,返回任务摘要,查询不执行或重试任务。验证记录见[本阶段报告](../reports/phase-3-task-queries.md)。
## 1. 权限与字段
请求使用 `Authorization: Bearer <token>`,需要当前命名空间的 `tasks:read` 及指定队列范围,不携带幂等键。命名空间由凭据确定,不接受调用方指定。队列不在权限范围返回403;有权限但不存在该队列/无匹配任务时返回空列表,不披露其他命名空间资源。
| 参数 | 类型与默认 | 要求 |
| --- | --- | --- |
| queue_id | string,必填 | 32位小写十六进制队列标识 |
| state | string,可选 | WAITING、LEASED、RUNNING、NEEDS_REVIEW、SUCCEEDED、FAILED、CANCELLED之一;省略表示所有状态 |
| business_key | string,可选 | 1–191个Unicode字符,无NUL/回车/换行;精确关联匹配,显式空值拒绝 |
| created_from | RFC3339时间,可选 | 创建时间下界,包含该时刻 |
| created_before | RFC3339时间,可选 | 创建时间上界,不包含该时刻;与下界同时给出时必须更晚 |
| limit | integer,默认20 | 1–100;可以在翻页时更改 |
| cursor | string,可选 | 上页next_cursor,最长1024字符,必须保持其他筛选不变 |
参数只能出现一次,未知名称、非法百分号编码、空时间、日期字符串、非规范状态或非法范围返回400。时间允许RFC3339时区偏移并规范化UTC;精度最多到微秒,UTC年份2000–9998。上下界可以仅给一个,不设置默认时间窗口。JSON的null不是URL查询值,省略参数才表示不设该条件。
business_key区分大小写、重音字符、Unicode编码形式以及尾部空格。例如 `object:1`、`object:1 `、`Object:1` 是不同键;`é` 与 `e` 加组合重音也不同。不修剪/规范化输入,不使用LIKE,`%` 和 `_` 是普通字符;关联键不承担业务幂等。纯空格键若已提交也按原值匹配,空字符串提交仍表示没有关联键;本接口不提供“只查无关联键”筛选。
## 2. 完整请求和响应
使用URL编码,尤其是键中的 `+`、`%`、空格和时间中的时区加号:
```sh
curl --get "$ORBIT_URL/v1/tasks" \
-H "Authorization: Bearer $ORBIT_TOKEN" \
--data-urlencode "queue_id=$ORBIT_QUEUE_ID" \
--data-urlencode 'business_key=object:operation-001' \
--data-urlencode 'state=FAILED' \
--data-urlencode 'created_from=2026-09-17T00:00:00Z' \
--data-urlencode 'created_before=2026-09-18T00:00:00Z' \
--data-urlencode 'limit=20'
```
HTTP200完整示意响应:
```json
{
"request_id":"44444444444444444444444444444444",
"data":{
"items":[{
"id":"22222222222222222222222222222222",
"queue_id":"11111111111111111111111111111111",
"state":"FAILED",
"revision":"4",
"handler":"object.prepare",
"handler_version":"v1",
"business_key":"object:operation-001",
"available_at":"2026-09-17T00:00:00Z",
"deadline_at":"2026-09-17T00:01:00Z",
"attempt_count":1,
"created_at":"2026-09-17T00:00:00Z",
"updated_at":"2026-09-17T00:00:01Z"
}]
}
}
```
有下一页时data还包含next_cursor字符串;没有匹配时items为 `[]`。摘要不携带payload/result/冻结策略/retry_of,按需调用 `GetTask` 获取详情。字段完整说明见[任务接口](tasks.md)。不返回全量总数或大offset接口。
## 3. 分页与并发语义
固定按 `(created_at DESC,id DESC)` 排序。相同创建时间以任务ID打破平局;取消、执行和核查改变状态/updated_at,不改变创建排序键。每页取至多limit+1行以判断下一页。
v2游标同时绑定命名空间、队列、状态、精确业务键和规范化UTC时间界限;长业务键通过固定长度摘要绑定,191字符的合法键不会撑破游标上限。修改/删除任何筛选、混用其他命名空间或传入非法游标返回400,不能通过更换参数扩大原页查询。游标是排序位置,不是授权证明,每页都重新校验当前凭据和队列范围。
跨页不是数据库快照:遍历期间新增任务、任务状态变化可能影响后续页面,不能将动态状态查询当作完整审计导出。新任务排在已读位置之前时,从首页刷新才会看到;状态筛选的任务离开该状态时可能从后续页面消失。需要可靠处理结果使用[结果订阅](subscriptions.md),核对历史使用[执行历史](execution-history.md)与[操作审计](task-recovery.md)。
旧版未带版本的游标仅兼容原队列/状态查询,不能附加业务键或时间界限。SDK把游标当不透明字符串,不应解析、改写或自行拼接。
## 4. SDK调用
`Client.ListTasks(ctx, orbit.ListTasksRequest)` 返回 `TaskPage`。新增字段BusinessKey、CreatedFrom、CreatedBefore,其余字段保持不变。BusinessKey空字符串在SDK中表示省略,时间指针nil表示无该界限。
```go
query := orbit.ListTasksRequest{
QueueID: queueID,
State: orbit.TaskFailed,
BusinessKey: "object:operation-001",
CreatedFrom: &from,
CreatedBefore: &before,
Limit: 20,
}
for {
page, err := client.ListTasks(ctx, query)
if err != nil { return err }
for _, task := range page.Items {
fmt.Println(task.ID, task.State, task.CreatedAt)
}
if page.NextCursor == "" { break }
query.Cursor = page.NextCursor
}
```
片段假设已有客户端及time.Time类型from/before;完整可运行源码见[示例](../../sdk/go/examples/task-query/main.go)。SDK校验ID、范围、状态、键长度/UTF-8及时间精度,并用url.Values编码,时间转UTC,不修改调用方时间对象。超时只影响读取,可保持条件重新请求;没有自动重试或自动翻页。
## 5. 存储、升级与边界
迁移000016明确business_key使用 `utf8mb4_0900_bin`,保证尾部空格有意义;现有文本不重写、不修剪,不增加模型字段或数据库外键。单项受理、计划触发和人工重试按Go字符串是否为空决定NULL,不依赖连接排序规则的NULLIF比较。
查询使用四个固定索引之一,全部将等值过滤放在创建时间及ID之前:
| 筛选组合(均含命名空间与队列) | 索引 |
| --- | --- |
| 无状态/业务键 | ix_task_created |
| 状态 | ix_task_state_created |
| 业务键 | ix_task_queue_business_created,新增 |
| 业务键与状态 | ix_task_queue_business_state_created,新增 |
上下界与游标在索引时间范围内继续扫描,不按全部任务取回后内存过滤。SQL索引名来自固定服务端分支,查询值全部参数化。实际MySQL执行计划验证四组走对应range/ref索引且无filesort;这是查询路径验证,不是生产吞吐或百万租户容量证明。
升级需先停止旧写入节点、应用迁移,再统一启用新版节点;列排序规则和索引调整会有DDL成本,应演练目标规模维护窗口。新SDK查询旧节点时,新参数会被拒绝,不能删除筛选后自动降级。新旧节点混合分页与生产滚动升级尚未验收。
通用400/401/403/503错误见[任务手册](tasks.md)。业务键与时间查询不新增权限,不授予执行、取消或人工核查权限。