Files
orbit/docs/api/application-resources.md

162 lines
11 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.
# 应用级共享配额
状态:P3 已实现。应用下所有命名空间和队列共享未终结任务额度、执行并发和开始速率;与 [队列控制](queues.md) 同时生效。研发接入仍使用独立 Go SDK,额度不依赖内存、外部队列或缓存。
## 1. 作用域与权限
| 操作 | HTTP | 权限 |
| --- | --- | --- |
| 当前应用配置及用量 | `GET /v1/application/resources` | `application:resources:read` |
| 完整替换三个上限 | `POST /v1/application/resources:configure` | `application:resources:manage` |
应用由 Bearer 凭据所属命名空间推导,不能通过 URL 或请求体指定其他应用。应用级动作独立授权,允许查看或管理该应用跨命名空间的聚合额度,不受 `queues` 列表缩小范围;普通 `queues:manage`、`workers:consume` 或 `queues:["*"]` 都不会自动获得应用级权限。通常只给运维管理凭据授予应用动作,业务 Worker 不需要这些权限。
初始化 CLI 显式传 `--resource-access` 才授予这两项权限。已有凭据不会自动扩权。`--application-id` 可在指定现有且启用的应用中创建新命名空间,与 `--application` 二选一;它仍要求数据库管理权限,不是公共 HTTP 开放的跨命名空间创建入口。
## 2. 查询配置及用量
```sh
curl "$ORBIT_URL/v1/application/resources" \
-H "Authorization: Bearer $ORBIT_TOKEN"
```
不接受查询参数。成功 HTTP 200:
```json
{
"request_id": "11111111111111111111111111111111",
"data": {
"application_id": "22222222222222222222222222222222",
"revision": "1",
"max_unfinished_tasks": 1000000,
"unfinished_tasks": "5",
"max_concurrency": 10000,
"active_attempts": "2",
"pending_starts": "1",
"rate_per_second": 100000
}
}
```
- `unfinished_tasks`:WAITING、LEASED、RUNNING、NEEDS_REVIEW 的任务总数。待核查仍占用额度;自动重试不重复占用。
- `active_attempts`:已预占或运行的尝试总数;未 Start 的尝试也占并发。每次权威结果或到期恢复只释放一次。
- `pending_starts`:已预占但未开始的尝试,是活跃尝试的子集。跨窗口仍保留预占,到期恢复或取消才释放未使用预占。
- `revision`:仅配置变更增加。用量变化不使配置版本失效;查询返回单次数据库读取快照,之后可能继续变化。
- 版本和累计用量用十进制字符串,SDK 解为 `uint64`;三个配置上限为 JSON 整数。
默认值是开发基线,不代表实测容量。
## 3. 配置及幂等
请求头必须有 `Authorization`、`Content-Type: application/json` 和 `Idempotency-Key`。键为 1–128 个非空白可打印 ASCII 字符;每次不同管理操作使用不同键,未确认结果重报保留原键和原内容。
| 字段 | 类型 | 要求 |
| --- | --- | --- |
| expected_revision | 十进制字符串 | 必填,非零,匹配当前配置版本 |
| reason | 字符串 | 必填,非空白,最多 1024 个 Unicode 字符,写入审计 |
| max_unfinished_tasks | uint32 | 必填,1–100000000;默认 1000000 |
| max_concurrency | uint32 | 必填,1–1000000;默认 10000 |
| rate_per_second | uint32 | 必填,1–10000000;默认 100000 |
这是完整替换,缺项或零值不能解释为沿用旧值。未知字段、重复 JSON 键、错误大小写及 null 被拒绝。
```sh
curl -X POST "$ORBIT_URL/v1/application/resources:configure" \
-H "Authorization: Bearer $ORBIT_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: application-budget-change-001' \
--data '{"expected_revision":"1","reason":"调整应用资源预算","max_unfinished_tasks":2000000,"max_concurrency":2000,"rate_per_second":5000}'
```
成功 HTTP 200:
```json
{
"request_id": "33333333333333333333333333333333",
"data": {
"resources": {
"application_id": "22222222222222222222222222222222",
"revision": "2",
"max_unfinished_tasks": 2000000,
"unfinished_tasks": "5",
"max_concurrency": 2000,
"active_attempts": "2",
"pending_starts": "1",
"rate_per_second": 5000
},
"deduplicated": false
}
}
```
配置、版本、管理审计、事件和幂等回执在一个事务中提交。相同键与内容返回原成功快照,`deduplicated=true`,其中用量也保留原值;实时用量应重新 GET。审计/事件失败则整体回滚。同一应用下两个命名空间的幂等键空间相互独立,共享配置版本;并发修改以 CAS 判定,不能借另一个命名空间重放管理身份。
降额不删除任务、不撤销已取得的并发、不重置速率窗口,也不延长租约。用量可暂时高于新上限。新受理和新领取会停止,等待已有任务释放;已取得执行并发的尝试仍可在有效租约内开始,但 Start 必须满足当前开始速率。
## 4. 执行与背压
- 直接提交和定时计划生成任务同时检查应用与队列积压上限。两者都允许才占用空间;任一失败不能留下另一侧的计数、任务或幂等记录。
- 原受理回执重报先核对身份,满额也能返回原回执。满额失败未受理,调用方退避后可用原身份再次提交;SDK 不自动重试写请求。
- 每次 Claim 同时受应用并发、队列并发、Worker 容量和两级开始窗口余量约束。没有额度时不派发、不增加尝试次数。
- Start 使用数据库时间的一秒固定窗口:第一次成功授权建立窗口;之后有效开始计数增加,重复回执和续租不计数。应用与队列窗口分别维护,边界可能出现短时突发,不承诺任意滑动一秒内都不超限,也不控制处理器内部的业务请求速率。
- 尚未 Start 的预占也计入窗口可用空间,避免其他队列把额度再次领走。配置降速后,既有预占可能超过新速率;Start 返回 `RATE_LIMITED`,自动 Worker SDK 仅在有效租约内使用原身份退避核对,过期按正常恢复处理。
- 运行中取消只保存意图,收到权威结果或到期恢复才释放并发;效果未知进入 NEEDS_REVIEW,继续占用未终结任务名额。应用停用后拒绝新用户操作,后台恢复仍可回收已到期尝试。
- 计划生成时满额,已成功处理的批次部分提交,未受理的原触发点保留,下一轮按冻结的计划策略处理,不把满额伪造为漏跑跳过。窗口超过策略期限后仍可能按原漏跑策略跳过。
## 5. 错误与重报
| 错误 | HTTP / WS | 处理 |
| --- | --- | --- |
| INVALID_ARGUMENT | HTTP 400 | 校正字段,不盲目重报 |
| UNAUTHENTICATED | HTTP 401 | 核对秘密、过期/撤销、应用及命名空间启停 |
| PERMISSION_DENIED | HTTP 403 | 申请明确应用动作;队列管理不能代替 |
| STATE_CONFLICT | HTTP 409 | 查询新配置,确认意图后以新版本及新键发起操作 |
| IDEMPOTENCY_CONFLICT | HTTP 409 | 保留原操作身份,查明请求内容为何改变 |
| APPLICATION_FULL | HTTP 429,`Retry-After: 1` | 当前应用满额,退避后保留原受理身份核对 |
| QUEUE_FULL | HTTP 429,`Retry-After: 1` | 当前队列满额,处理同上 |
| RATE_LIMITED | Worker 错误帧 | 应用或队列 Start 限流,保留消息身份及租约边界 |
| UNAVAILABLE / 网络结果未知 | HTTP 503 / 连接错误 | 保留原键、版本和内容核对,不能假定写入失败 |
`APPLICATION_FULL` 来自任务受理接口,额度管理本身不会因满额而被禁止。两级积压同时满时优先报告应用满额。`Retry-After` 不是空间保证。
## 6. SDK 与可运行场景
SDK 方法:`GetApplicationResources(ctx)` 和 `ConfigureApplicationResources(ctx, key, request)`。完整双命名空间场景源码:[application-resources](../../examples/application-resources/main.go)。
在仓库根目录,先用已迁移数据库启动服务,并初始化一个独立、空闲的示例应用:
```sh
mkdir -p .local
chmod 700 .local
go run ./cmd/orbit-admin bootstrap \
--application application-resources-demo --namespace first --environment development \
--queue first --resource-access --output .local/app-first.credentials.json
ORBIT_EXAMPLE_APP_ID=$(sed -n 's/.*"application_id":"\([^"]*\)".*/\1/p' .local/app-first.credentials.json)
go run ./cmd/orbit-admin bootstrap \
--application-id "$ORBIT_EXAMPLE_APP_ID" --namespace second --environment development \
--queue second --resource-access --output .local/app-second.credentials.json
cd examples
ORBIT_URL=http://127.0.0.1:8080 \
ORBIT_CREDENTIALS_FILE=../.local/app-first.credentials.json \
ORBIT_SECOND_CREDENTIALS_FILE=../.local/app-second.credentials.json \
go run ./application-resources
```
预期:
```text
cross_namespace_backpressure=APPLICATION_FULL http=429
same_identity_retry_accepted=true
unfinished=0 active=0 pending_starts=0
```
示例将应用上限设为 1:命名空间 first 受理任务 → second 被全局满额拒绝 → first 取消释放 → second 以原键成功受理 → 取消收尾。正常或报错退出都会尝试按原配置版本恢复上限,遇到其他管理者修改则报冲突,不覆盖新配置。进程被强杀不能保证运行清理;未收尾任务会按总截止时间恢复,结果未知时仍应保留原身份查询。无业务 Worker 和外部副作用,资源及历史保留,不提供硬删除。
## 7. 存储、升级与边界
000010 新增 `orbit_application_resources`,应用标识使用既有 `BINARY(16)`,全部模型及字段提供中文注释,无外键。回填所有应用的未终结、活跃及预占数,空应用为零;历史任务、尝试、队列配置不改写。停止全部旧写入节点 → 执行迁移 → 启动新版;旧版不维护应用计数,不能混跑。大表回填时间和在线迁移仍需部署环境验证。
统一顺序为认证 → Worker 会话(需要时)→ 应用额度 → 队列 → 任务/尝试 → 许可。额度与身份分表,避免把认证共享锁升级为排他锁。恢复和调度对忙碌额度行使用 SKIP LOCKED,保留后续扫描;不采用全表计数参与每次领取。额度配置审计记录在发起凭据的命名空间,应用标识明确保存。
同一应用共享一条额度行,严格额度会使该应用的相关短事务串行化。当前范围是同一 MySQL 主库;未宣称跨库原子配额或百万租户性能,后续容量验证需测单个高流量应用的锁等待、租约续期和恢复延迟。[资源键许可](keyed-concurrency.md) 和 [优先级/节点公平领取](priority-and-fairness.md) 已接入;可靠订阅与完整生产容量/故障验收继续按开发计划推进。真实验证记录见 [应用配额回归](../reports/phase-3-application-resources.md)。