feat: enforce shared application resource quotas across namespaces
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 数据模型与实现基线
|
||||
|
||||
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度和队列限速/背压;应用配额、公平性、订阅及流程编排继续开发。
|
||||
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度、队列限速/背压和应用配额;公平性、订阅及流程编排继续开发。
|
||||
|
||||
## 1. 模块与依赖
|
||||
|
||||
@@ -35,11 +35,12 @@
|
||||
|
||||
## 3. 实体清单
|
||||
|
||||
当前共 32 个实体:初始迁移包含 30 个,000003 增加进程会话头和迟到证据。迁移自身另有 `orbit_schema_migrations` 记录。
|
||||
当前共 33 个实体:初始迁移包含 30 个,000003 增加进程会话头和迟到证据,000010 增加应用共享额度。迁移自身另有 `orbit_schema_migrations` 记录。
|
||||
|
||||
| 模块 | Go 模型 | 数据表 |
|
||||
| --- | --- | --- |
|
||||
| platform | `Application` | `orbit_applications` |
|
||||
| platform | `ApplicationResources` | `orbit_application_resources` |
|
||||
| platform | `Namespace` | `orbit_namespaces` |
|
||||
| platform | `Credential` | `orbit_credentials` |
|
||||
| platform | `IdempotencyKey` | `orbit_idempotency_keys` |
|
||||
@@ -127,3 +128,7 @@ P2 服务端已新增会话隔离、两步派发、租约、并发许可、结
|
||||
000009_queue_resources 增加 Queue.MaxUnfinishedTasks、UnfinishedTasks、StartWindowUntil、StartsInWindow,Go 字段与 SQL COMMENT 均有中文语义,实体仍为 32。未终结任务按 WAITING/LEASED/RUNNING/NEEDS_REVIEW 回填;窗口初始为空、计数为零。运行计数与任务/开始授权同事务维护,不改变队列配置 revision,配置变更不能重置它们。降额允许已有使用量暂时超过新上限,不删除任务或撤销已有许可;详见 [队列契约](api/queues.md) 和 [资源回归](reports/phase-3-queue-resources.md)。
|
||||
|
||||
命令与验证记录见 [开发计划](08-development-plan.md)、[基础阶段验证报告](reports/phase-0.md) 与 [取消及无外键回归](reports/phase-3-cancellation.md)。历史阶段报告描述当时结构,当前约束以本页为准。
|
||||
|
||||
## 12. 应用共享额度
|
||||
|
||||
000010_application_resources 新增 ApplicationResources,使用 BINARY(16) 应用主键,12 个持久化字段均有中文注释和 SQL COMMENT,无外键。共享未终结任务数、活跃尝试数、未开始预占数与一秒开始窗口;配置 revision 不随用量变化。停止旧写入节点后回填所有应用,包含跨命名空间的既有在途状态。关联身份行与额度行分开,认证后按应用额度 → 队列 → 任务/尝试 → 许可顺序持锁。后台恢复不因应用停用而停止释放到期尝试。详细字段、权限、SDK 场景及并发边界见 [应用配额接口](api/application-resources.md)。
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
| P0 基础与数据模型 | Go workspace、独立 SDK module、公共/Pulse/Weave 模型、MySQL 迁移、配置、健康检查 | 编译/竞态/静态检查;真实 MySQL 建表、隔离、唯一性、预算、不可变版本、迁移失败防护;SDK 健康调用 | 已完成并通过阶段回归(见 P0 报告) |
|
||||
| P1 任务受理闭环 | 应用凭据与作用域权限、队列、幂等提交、查询、待执行取消、受理事件;SDK 客户端 | API/SDK 创建查询闭环;重复键与内容冲突、响应未确认重报、跨命名空间拒绝、参数边界 | 已完成并通过阶段回归(见 P1 报告;真实断路/提交 ACK 丢失注入留后续故障阶段) |
|
||||
| P2 Worker 执行闭环 | WSS 接入、能力/容量、领取与开始、租约、续租、结果确认、排空与恢复;Worker SDK | 真 Worker 执行;失联/重连/重复确认/旧尝试拒绝;业务效果未知转核查;节点故障接管 | 已完成并通过阶段回归:服务端、自动 SDK、历史查询、完整 Schema 与真实进程强杀恢复;测试拓扑和剩余生产故障矩阵见 P2 验收报告 |
|
||||
| P3 Pulse 完整能力 | 安全重试、延时、Cron/固定间隔、漏跑策略、计划版本、运行中取消、并发许可、配额/背压、可靠结果订阅 | 多实例领取/触发竞争、重试上限、取消竞争、队列隔离、积压恢复、事件重复交付 | 进行中:安全重试、计划调度、运行中取消、队列控制/限速/积压背压及 SDK 场景已实现并回归;应用级配额、业务键许可、优先级/公平性和可靠订阅待开发 |
|
||||
| P3 Pulse 完整能力 | 安全重试、延时、Cron/固定间隔、漏跑策略、计划版本、运行中取消、并发许可、配额/背压、可靠结果订阅 | 多实例领取/触发竞争、重试上限、取消竞争、队列隔离、积压恢复、事件重复交付 | 进行中:安全重试、计划调度、运行中取消、队列控制/限速/积压背压、跨命名空间应用配额及 SDK 场景已实现并回归;业务键许可、优先级/公平性和可靠订阅待开发 |
|
||||
| P4 Weave 主流程 | SDK 定义构建与发布校验、实例、顺序/分支/结构化并行汇合、信号/定时等待、版本绑定 | 重启接续、早到/重复信号、并行竞争、内部事件重放、发布版本兼容;端到端通用流程 | 待开发 |
|
||||
| P5 Weave 恢复与查询 | 有界查询、暂停继续、取消、人工核查恢复、控制门控与审计 | 轮次与尝试独立预算;暂停/开始/回执/超时竞争;恢复保留预算;未知结果不盲目重复 | 待开发 |
|
||||
| P6 生产验证与运维 | 完整监控、凭据轮换、备份恢复、滚动升级、运行手册、可复现负载和故障工具 | 对照生产验收清单执行,报告环境/指标/遗留项;容量、RPO/RTO 按真实拓扑验证 | 待开发 |
|
||||
@@ -45,6 +45,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
|
||||
| 定期数据同步 | 发布计划 → 触发任务 → 暂停/恢复 → 查看触发记录 | P3,已实现通用快照示例 |
|
||||
| 运行中取消 | 保存取消意图 → 停止处理器 → 确认结果 → 查询并排空 | P3,已实现完整示例 |
|
||||
| 队列限速与背压 | 暂停 → 满额拒绝 → 原回执核对 → 释放空间 → 限速执行 | P3,已实现完整示例 |
|
||||
| 跨命名空间应用配额 | first 受理 → second 全局满额 → 释放 → 原键成功 → 恢复配置 | P3,已实现完整示例 |
|
||||
| 并行生成结果 | 发布流程 → 并行执行 → 汇合 → 完成 | P4 |
|
||||
| 审核与外部回执 | 提交流程 → 等待信号 → 授权信号注入 → 继续 | P4 |
|
||||
| 外部作业轮询 | 提交一次 → 有界查询处理中状态 → 回执或最终结果 → 超限核查 | P5 |
|
||||
@@ -82,6 +83,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
|
||||
- [P3 计划调度阶段记录](reports/phase-3-schedules.md)
|
||||
- [P3 取消与无外键回归](reports/phase-3-cancellation.md)
|
||||
- [P3 队列资源控制回归](reports/phase-3-queue-resources.md)
|
||||
- [P3 应用共享配额回归](reports/phase-3-application-resources.md)
|
||||
- [P2 服务端阶段记录](reports/phase-2-server.md)
|
||||
- [P2 SDK 协议阶段记录](reports/phase-2-sdk-protocol.md)
|
||||
- [P2 自动 Worker 运行器记录](reports/phase-2-worker-runtime.md)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
- 文档版本:0.2
|
||||
- 整理日期:2026-09-17
|
||||
- 阶段:P0–P2 已通过阶段验收;P3 安全重试、计划调度、运行中取消和队列资源控制已通过回归,数据库已改为无外键,P3–P7 继续推进,尚未完成全平台生产验收
|
||||
- 阶段:P0–P2 已通过阶段验收;P3 安全重试、计划调度、运行中取消、队列资源控制和应用共享配额已通过回归,数据库已改为无外键,P3–P7 继续推进,尚未完成全平台生产验收
|
||||
- 适用范围:Orbit 统一平台,包含 Pulse 任务调度模块、Weave 流程编排模块与公共能力。平台面向多个业务系统复用。
|
||||
|
||||
## 阅读顺序
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
| `/v1/tasks/{task_id}/attempts` 与尝试详情 | P2 已实现 | [执行历史](execution-history.md) |
|
||||
| `/v1/tasks/{task_id}/attempts/{attempt_id}/evidence` 与证据详情 | P2 已实现 | [执行历史](execution-history.md) |
|
||||
| `/v1/queues` 创建、查询及 `:configure` 控制 | 已实现队列并发、开始速率、积压背压与暂停/恢复 | [队列接口与 SDK 场景](queues.md) |
|
||||
| `/v1/application/resources` 查询及 `:configure` 管理 | P3 已实现跨命名空间应用配额 | [应用配额与 SDK 场景](application-resources.md) |
|
||||
| 凭据校验与初始化 CLI | P1 已实现 | [鉴权](authentication.md) |
|
||||
| `/v1/worker/connect` | P2 已实现并通过阶段验收 | [Worker 契约](worker.md)、[Schema](worker-schema.md) |
|
||||
| 任务冻结策略与 Worker `retryable` | P3 安全重试已实现 | [安全重试](retries.md) |
|
||||
|
||||
161
docs/api/application-resources.md
Normal file
161
docs/api/application-resources.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# 应用级共享配额
|
||||
|
||||
状态: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](../../sdk/go/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 sdk/go
|
||||
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 ./examples/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 主库;未宣称跨库原子配额或百万租户性能,后续容量验证需测单个高流量应用的锁等待、租约续期和恢复延迟。业务键许可、公平派发、可靠订阅与完整生产容量/故障验收继续按开发计划推进。真实验证记录见 [应用配额回归](../reports/phase-3-application-resources.md)。
|
||||
@@ -55,3 +55,5 @@ SDK 使用 `orbit.Options{Token: token}`。健康查询不发送凭据。当前
|
||||
P3 增加 schedules:read 和 schedules:manage。bootstrap 显式传 --schedule-access 才授予;创建/发布计划还需要 tasks:submit 与队列范围。已有计划属于命名空间,创建凭据到期或撤销不自动暂停计划;停止新触发应暂停计划,或禁用应用/命名空间。详见 [计划接口](schedules.md)。
|
||||
|
||||
P2 增加 workers:connect 和 workers:consume。bootstrap 只有显式提供 --worker-access 时才同时授予这两项开发权限;现有凭据不自动变化。Worker 还需独立的进程恢复秘密和尝试秘密,见 [Worker 契约](worker.md)。
|
||||
|
||||
P3 应用额度管理新增 `application:resources:read` 和 `application:resources:manage`。只有显式 `--resource-access` 才授予,作用域是所属应用的全部命名空间,普通队列管理不能替代。查询返回聚合计数,不开放其他命名空间的任务数据。`bootstrap --application-id <现有应用ID>` 可由数据库管理员增加命名空间,与 `--application` 二选一;验证应用启用及额度记录存在后,同事务创建新资源,所有秘密交付约定保持不变。应用默认未终结上限 1000000、并发 10000、开始速率 100000/秒;详见 [应用共享配额](application-resources.md)。
|
||||
|
||||
@@ -144,4 +144,4 @@ unfinished=0 rate_per_second=1 worker_drained=true
|
||||
|
||||
创建命令省略新字段时,幂等摘要保留旧字段集合。旧创建回执不含新字段,SDK 解码为零,只表示历史快照缺少信息,实时配置另行 GetQueue。配置接口是新能力;自动 SDK 已能处理 RATE_LIMITED,进行降额管理前应先升级 Worker。
|
||||
|
||||
本阶段验证见 [资源控制报告](../reports/phase-3-queue-resources.md)。仍需应用级跨命名空间配额、同业务键许可、可配置优先级及应用间公平性、可靠事件积压限制和容量验收;不能用单队列控制替代这些要求。
|
||||
本阶段验证见 [资源控制报告](../reports/phase-3-queue-resources.md)。[应用级跨命名空间配额](application-resources.md) 已实现并同时生效。仍需同业务键许可、可配置优先级及应用间公平性、可靠事件积压限制和容量验收;不能用单队列控制替代这些要求。
|
||||
|
||||
@@ -180,6 +180,7 @@ SDK:`Client.CancelTask(ctx, taskID, key, request)`。此操作不承诺回滚
|
||||
| 409 STATE_CONFLICT | 当前状态或版本不允许操作,先查询 |
|
||||
| 413 BODY_TOO_LARGE | 减小请求或改为传递业务对象引用 |
|
||||
| 415 UNSUPPORTED_MEDIA_TYPE | 使用 application/json |
|
||||
| 429 APPLICATION_FULL | 应用下所有命名空间共享额度已满;按 Retry-After 退避并保留原身份,详见 [应用配额](application-resources.md) |
|
||||
| 429 QUEUE_FULL | 本次未受理,按 Retry-After 有限退避并保留原身份;已受理的原回执不受当前满额限制 |
|
||||
| 503 UNAVAILABLE | 结果可能未知;保留原幂等身份,有限退避后核对 |
|
||||
|
||||
|
||||
@@ -127,11 +127,11 @@ receipt 是原操作的不可变确认;lease 是本次重新核验的执行权
|
||||
|
||||
## 5. 领取、并发与回收
|
||||
|
||||
平台在同一事务中分配队列并发许可、创建尝试、递增任务执行代次和次数、保存 LEASED 与派发事件。领取本身消耗一次 max_attempts 预算,包括最终未获开始授权的尝试,预算不会因重连或过期恢复重置。
|
||||
平台在同一事务中预占应用并发与开始额度、分配队列并发许可、创建尝试、递增任务执行代次和次数、保存 LEASED 与派发事件。领取本身消耗一次 max_attempts 预算,包括最终未获开始授权的尝试,预算不会因重连或过期恢复重置。
|
||||
|
||||
运行中的进程总容量跨其所有旧/新会话统计。声明容量减少、断开连接或重复结果,都不能重复释放额度。已实施队列 max_concurrency、rate_per_second 及未终结任务上限;参数含义、窗口突发、暂停及降额语义见 [队列资源控制](queues.md)。应用级配额、业务键许可与公平性仍在 P3 推进。
|
||||
运行中的进程总容量跨其所有旧/新会话统计。声明容量减少、断开连接或重复结果,都不能重复释放额度。已实施队列 max_concurrency、rate_per_second 及未终结任务上限;参数含义、窗口突发、暂停及降额语义见 [队列资源控制](queues.md)。[应用级配额](application-resources.md) 已同时生效;业务键许可与公平性继续推进。
|
||||
|
||||
领取前核对速率窗口及已有 LEASED 预留,满额时不新建尝试。Start 在同一队列事务内消费开始预算,原 ACK 重报不重复计数。领取后若配置降额,Start 可能返回 RATE_LIMITED;保持原消息身份在剩余租约内退避核对。续租、结果确认、取消及恢复不消费新开始额度。
|
||||
领取前核对速率窗口及已有 LEASED 预留,满额时不新建尝试。Start 在同一事务内消费应用与队列的开始预算,原 ACK 重报不重复计数。领取后若配置降额,Start 可能返回 RATE_LIMITED;保持原消息身份在剩余租约内退避核对。续租、结果确认、取消及恢复不消费新开始额度。
|
||||
|
||||
每个节点每 2 秒发起一批恢复扫描,每批最多 100 项:
|
||||
|
||||
|
||||
@@ -81,6 +81,15 @@
|
||||
- 异常:网络结果未知保留原身份,降额不清零用量,待核查任务仍占名额;跨节点及重启证据见 [资源回归](../reports/phase-3-queue-resources.md)。
|
||||
- 清理:每次新建独立队列,任务 20 秒总截止;无外部副作用,历史保留,失败非零退出并有界收尾。
|
||||
|
||||
## 已可运行:跨命名空间共享配额
|
||||
|
||||
- 源码:[应用资源示例](../../sdk/go/examples/application-resources/main.go),[双命名空间初始化、字段和输出](../api/application-resources.md)。
|
||||
- 前置:同一独立、空闲示例应用下的两个命名空间,凭据显式具备应用资源管理权限;不需要 Worker。
|
||||
- 步骤:应用上限设为 1 → first 受理 → second 全局满额 → 取消 first → second 以原受理键成功 → 取消收尾并按 CAS 恢复配置。
|
||||
- 预期:APPLICATION_FULL/429、same_identity_retry_accepted=true、三个用量归零。
|
||||
- 异常:普通队列管理员不能改应用额度;恢复配置遇到新版本冲突时不覆盖。进程强杀后不能保证清理,遗留任务受总时限约束,须按原身份核对。
|
||||
- 清理:正常完成取消示例任务并恢复配置;历史保留,无业务副作用、无硬删除。
|
||||
|
||||
## 计划场景与验收标准
|
||||
|
||||
| 场景 | 输入与步骤 | 预期结果 | 必测异常 | 状态 |
|
||||
|
||||
94
docs/reports/phase-3-application-resources.md
Normal file
94
docs/reports/phase-3-application-resources.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# P3 应用级配额与跨命名空间回归
|
||||
|
||||
验证日期:2026-09-17。本阶段补齐应用共享的未终结任务上限、执行并发及开始速率,同步 HTTP、独立 Go SDK、CLI 和双命名空间示例。P3 业务键许可、公平性及可靠订阅,以及 P4–P7 仍未完成,持续目标保持进行中。
|
||||
|
||||
## 1. 交付范围
|
||||
|
||||
- [应用配额接口](../api/application-resources.md):读取当前应用额度及用量、显式应用管理权限、CAS/幂等管理、同事务审计和事件、降额保留在途任务。
|
||||
- [ApplicationResources 模型](../../platform/model/application_resources.go) 与 000010 前向迁移:12 个持久化字段提供中文注释,SQL 同步 COMMENT,应用主键 BINARY(16),数据库外键仍为零,当前模型共 33 个。
|
||||
- 受理、计划生成、领取、Start、取消、Finish、安全重试及到期恢复共同维护应用计数,跨命名空间与队列共享。空轮询与失败受理不消耗计数,重复回执不重复计费。
|
||||
- 额度行与认证身份分离,统一按应用额度 → 队列 → 任务/尝试 → 许可顺序取锁;后台扫描跳过忙碌额度,应用停用后仍可释放到期尝试。
|
||||
- 管理 CLI 支持 `--application-id` 在已有启用应用下增加命名空间;应用管理权限必须显式 `--resource-access`,普通队列及 Worker 凭据不扩权。
|
||||
- [双命名空间 SDK 示例](../../sdk/go/examples/application-resources/main.go):第一侧受理,第二侧 APPLICATION_FULL,第一侧取消释放,第二侧原身份重报成功,取消收尾并按 CAS 恢复配置。
|
||||
|
||||
## 2. 环境和实际回归
|
||||
|
||||
macOS 本机,Go 1.27.1,Docker MySQL 8.4.10;服务进程与数据库仅监听回环地址。使用随机凭据及一次性数据库,测试结束删除测试容器,没有修改已有业务数据库。
|
||||
|
||||
执行入口:
|
||||
|
||||
```sh
|
||||
make fmt test vet build
|
||||
make integration
|
||||
```
|
||||
|
||||
完整集成通过。测试分组的本次结果:
|
||||
|
||||
| 分组 | 结果 |
|
||||
| --- | --- |
|
||||
| 全部数据库集成(含模型、无外键、配额、计划、执行及竞争) | PASS,68.475 秒 |
|
||||
| v9 应用计数回填与在途释放 | PASS,3.94 秒 |
|
||||
| v7 无外键升级、索引及数据保留 | PASS,3.85 秒 |
|
||||
| SDK Worker 连接、运行器、重试、取消和断线处理 | PASS,78.509 秒 |
|
||||
| SDK 计划生命周期 | PASS,含包开销 4.733 秒 |
|
||||
| SDK 队列资源控制 | PASS,含包开销 4.456 秒 |
|
||||
| SDK 双命名空间应用配额 | PASS,含包开销 1.401 秒 |
|
||||
| 原有真实进程故障组 | PASS,23.638 秒 |
|
||||
| 十个可运行示例 | 全部通过 |
|
||||
|
||||
十个示例为 health、tasks、worker-protocol、worker、history、retry、schedules、cancellation、resources、application-resources。上述用时仅为回归耗时,不是吞吐或容量结论。未配置集成环境的普通 `go test` 会跳过外部数据库及进程用例,实际证据来自已执行的集成入口。
|
||||
|
||||
本轮发现原计划版本边界用例使用宿主机时钟等待,而服务按数据库时间判定。用例改为从 MySQL 时间建立边界并等待真实数据库时刻,保留原版本、触发身份和结束状态断言;完整数据库回归随后通过。
|
||||
|
||||
## 3. 配额与事务证据
|
||||
|
||||
[数据库用例](../../internal/storage/mysql/application_resources_integration_test.go) 覆盖:
|
||||
|
||||
1. 普通队列凭据读取及管理应用额度被拒绝;显式授权后同应用另一个命名空间可读聚合额度,其他应用保持独立。
|
||||
2. 配置 CAS、同身份原快照重报、内容冲突;审计故障注入使配置、事件及幂等记录一起回滚,恢复后原身份可成功提交。
|
||||
3. 两个命名空间各自队列同时发起 16 次受理,上限为 3 时仅 3 次成功;满额原成功回执仍可核对。降额至 1 不删已有任务,逐项取消后重新受理。
|
||||
4. 队列已满和事件写入失败均不能泄漏应用额度;应用与队列计数同时回滚,原请求可以重报。
|
||||
5. 两个节点/命名空间争用应用并发 1,仅一个取得尝试;预占取消释放未开始额度,成功结果及重复 Finish 不重复释放。
|
||||
6. 预占后降低并发及开始速率,已有尝试保留;Start 事件故障回滚用量;原 Start ACK 重报不收费;未开始的预占跨窗口保留,不能被其他任务抢占。
|
||||
7. 明确失败重试不重复占用积压,已开始失联保留未知效果及未完成额度,未开始到期和待执行截止释放空间。应用停用不阻止后台回收执行额度。
|
||||
8. 定时计划遇到应用满额时保留未受理触发点,已生成的批次部分提交;释放空间后恢复同一原触发点,两级计数一致。
|
||||
9. 一个应用额度行被显式持锁时,另一应用的恢复仍能推进;缺失、停用或参数歧义的应用不能由初始化 CLI 创建新命名空间。
|
||||
|
||||
所有计数断言同时查询真实任务状态进行核对,不只断言接口里的同一计数字段。
|
||||
|
||||
## 4. 迁移和无外键
|
||||
|
||||
[应用额度升级测试](../../internal/storage/mysql/application_resources_upgrade_integration_test.go) 在隔离库建立有效任务、尝试、Worker 会话和许可,然后还原为 v9 结构再运行正式迁移:
|
||||
|
||||
- 同一应用两个命名空间包含 WAITING、LEASED、RUNNING、NEEDS_REVIEW 和已成功任务,回填为 unfinished=4、active=2、pending=1;成功任务排除在外。
|
||||
- 空应用三个用量均为零,配置默认值、版本与空窗口正确。
|
||||
- 历史任务状态及版本不变;重复执行迁移不重算或覆盖当前计数。
|
||||
- 迁移前已存在的预占取消和运行尝试完成后,共享用量正确变为 unfinished=2、active=0、pending=0。
|
||||
|
||||
v4 升级继续核对冻结策略、未知标记及应用未完成数。v7 无外键升级仍移除全部 70 个历史外键,已有 325 条索引列定义保留;新应用额度表自身主键另计。错误队列归属、错误会话关系及手工外键漂移继续被拒绝。
|
||||
|
||||
## 5. SDK 与进程验证
|
||||
|
||||
[SDK 集成用例](../../sdk/go/application_resources_integration_test.go) 通过实际 HTTP 验证两个命名空间共享配置、普通队列管理员无应用权限、跨命名空间幂等键空间独立、错误字段及应用归属覆盖被拒绝。第二侧收到 HTTP 429 / APPLICATION_FULL / Retry-After: 1;第一侧释放后,原键提交成功,最终三个用量为零。
|
||||
|
||||
示例实际输出:
|
||||
|
||||
```text
|
||||
cross_namespace_backpressure=APPLICATION_FULL http=429
|
||||
same_identity_retry_accepted=true
|
||||
unfinished=0 active=0 pending_starts=0
|
||||
```
|
||||
|
||||
原有服务 ACK 丢失接管、Worker 强杀后未知核查、计划强杀补跑和队列背压进程用例同步通过。
|
||||
|
||||
最后定向回归新增的额度泄漏断言与应用强杀场景也通过:数据库应用配额九组场景 10.24 秒(含包开销 11.572 秒),SDK 应用配额 0.15 秒,完整示例再次通过;`TestIntegrationWorkerProcessApplicationBackpressure` 0.14 秒(含包开销 1.818 秒)。该场景强杀并 Wait 确认旧服务退出,再启动不同 node_id:共享上限和未完成计数保留,原受理回执可重放,另一个命名空间继续被全局满额拒绝,原任务取消后才能用被拒原键受理。这项证明应用受理额度跨进程持久化,不将它扩大为全部数据库故障模型或开始速率窗口的进程验收。
|
||||
|
||||
文档校验通过 320 个本地链接及 26 个 JSON 示例;269 个直接声明的持久化字段全部有中文注释。
|
||||
|
||||
## 6. 当前边界
|
||||
|
||||
- 共享配额限于同一 MySQL 主库。单个应用的额度行是串行化点,必须在生产容量阶段测锁等待、吞吐、续租与恢复延迟;没有宣称百万租户容量或跨库原子配额。
|
||||
- 开始限流为固定一秒窗口,允许边界突发;控制的是平台授权次数,不是处理器内部业务请求。
|
||||
- 停止旧写入节点后迁移再启动新版,不能与不维护应用用量的旧节点混跑。大表回填和混合版本在线升级未验收。
|
||||
- 数据库关联依靠服务端事务,没有外键;直接 SQL 修改归属或删除额度记录不属于支持的业务操作。
|
||||
- 本轮为单数据库、多服务进程及故障注入验证;数据库复制切换、跨故障域网络分区、备份恢复、实际 RPO/RTO 和峰值容量仍在 P6。
|
||||
@@ -179,4 +179,4 @@ task=<任务ID> state=SUCCEEDED attempts=1 evidence=1 reason=RESULT_CONFLICT dis
|
||||
|
||||
## 7. 后续交付
|
||||
|
||||
P2 已提供底层协议、[自动 Worker 运行器及示例](worker-runtime.md) 与历史查询,并通过 [Schema 及进程故障阶段验收](../reports/phase-2.md);P3 已实现 [安全重试](../api/retries.md)、[计划调度](../api/schedules.md)、[运行中取消](../api/cancellation.md) 和 [队列资源控制及示例](../api/queues.md),应用级配额、业务键许可、优先级/公平性和结果订阅继续开发;P4–P5 提供流程构建发布、信号、等待、轮询与人工恢复。每项随实现同步交付可运行示例及异常回归。
|
||||
P2 已提供底层协议、[自动 Worker 运行器及示例](worker-runtime.md) 与历史查询,并通过 [Schema 及进程故障阶段验收](../reports/phase-2.md);P3 已实现 [安全重试](../api/retries.md)、[计划调度](../api/schedules.md)、[运行中取消](../api/cancellation.md) 、[队列资源控制及示例](../api/queues.md) 和 [应用共享配额及示例](../api/application-resources.md),业务键许可、优先级/公平性和结果订阅继续开发;P4–P5 提供流程构建发布、信号、等待、轮询与人工恢复。每项随实现同步交付可运行示例及异常回归。
|
||||
|
||||
Reference in New Issue
Block a user