92 lines
8.0 KiB
Markdown
92 lines
8.0 KiB
Markdown
# P3 优先级与节点公平派发回归
|
||
|
||
验证日期:2026-09-17。完成 [任务优先级、等待补偿、节点应用/队列轮转及 SDK 场景](../api/priority-and-fairness.md)。P3 可靠订阅、P4–P5 Weave、P6 生产负载/故障验收和 P7 最终交付核对仍未完成,持续目标保持进行中。
|
||
|
||
## 1. 交付内容
|
||
|
||
- POST /v1/tasks、查询/分页、计划模板、Worker 派发及独立 Go SDK 接入 Priority。值为 0–9,默认 0,随受理冻结、随安全重试保留。
|
||
- [000012 迁移](../../internal/storage/mysql/migrations/000012_dispatch_priority.up.sql) 新增只读生成字段 DispatchAt 和排序索引,对既有 priority 增加范围 CHECK 和中文 COMMENT;生成时间等于 available_at 减 priority × 30 秒。真正的就绪时间、截止、权限、能力、门控和许可条件保持生效。
|
||
- 以最多 270 秒的排序优势实现优先级加速;等待超过此优势的旧任务不被不断到来的最高级新任务持续插队。没有承诺 270 秒内完成或严格 FIFO。
|
||
- [节点领取池](../../internal/dispatch/pool.go) 将原来的各连接并发轮询改为应用 → 队列 → 连接请求轮转。总并发 4、单应用在途 1、待处理加在途最多 128;每连接最多一个未完成请求。应用归属由认证后的数据库关系取得,不能按自报应用或不同命名空间分组。
|
||
- 同命名空间/队列/精确能力的空探测在节点内合并一个周期,缓存最多 128 项;控制收发继续运行。所有任务状态、许可、预算和执行授权仍由数据库事务决定。
|
||
- [可运行示例](../../examples/priority/main.go)、协议 Schema、接口与 SDK 文档同步更新。当前仍为 33 个实体,274 个直接声明持久化字段全部有中文注释,最终数据库外键为零。
|
||
|
||
## 2. 环境和执行
|
||
|
||
macOS 本机、Go 1.27.1、Docker MySQL 8.4.10、回环 HTTP/WSS 和一次性数据库。测试使用随机私密凭据,结束清理自建容器,不连接既有业务库。
|
||
|
||
```sh
|
||
make fmt test vet build
|
||
make integration
|
||
cd sdk/go
|
||
GOWORK=off go test -race ./...
|
||
```
|
||
|
||
上述检查通过。真实数据库、SDK、示例和进程故障来自 scripts/test-mysql.sh;普通未配置环境的 go test 会跳过这些外部用例,不能替代完整集成。
|
||
|
||
| 分组 | 本次结果 |
|
||
| --- | --- |
|
||
| 全部 MySQL 集成 | PASS,74.570 秒 |
|
||
| 优先级四组数据库场景 | PASS,0.56 秒 |
|
||
| 公平领取应用隔离 | PASS,1.43 秒 |
|
||
| SDK Worker 连接、运行器、重试、取消、优先级与断线 | PASS,88.849 秒 |
|
||
| SDK 优先级输入与执行顺序 | PASS,3.16 秒 |
|
||
| SDK 计划生命周期 | PASS,5.659 秒(含包开销) |
|
||
| SDK 队列 / 应用资源 | PASS,4.470 / 1.416 秒(含包开销) |
|
||
| 七项真实进程故障组 | PASS,28.296 秒 |
|
||
| 其中优先级强杀恢复 | PASS,3.14 秒 |
|
||
| 十二个完整 SDK 示例 | 全部通过 |
|
||
|
||
十二个示例:health、tasks、worker-protocol、worker、history、retry、schedules、cancellation、resources、keyed-concurrency、priority、application-resources。回归耗时不代表吞吐、延迟 SLO 或生产容量。
|
||
|
||
## 3. 优先级与数据库证据
|
||
|
||
[优先级数据库用例](../../internal/storage/mysql/priority_integration_test.go) 验证:
|
||
|
||
1. 9 级任务先于 5 秒前到期的 0 级任务领取;尚有一小时才到期的 9 级任务不被提前领取。详情、摘要及派发保留 priority。
|
||
2. 0 级任务已等待 271 秒时,即使候选前缀有 24 条新 9 级任务,也先取得原任务,避免候选数量截断造成的饥饿。
|
||
3. 明确失败进入退避后保留 9 级,数据库生成字段与 available_at 差 270 秒;退避前不领取,到期后第二次尝试正常成功。
|
||
4. 默认 0 的原键原内容继续取回原回执;同键改优先级冲突;负数和 10 被拒绝。直接写入超范围 priority 和独立修改 generated 字段均由数据库拒绝。
|
||
|
||
[计划及资源键集成](../../internal/storage/mysql/keyed_concurrency_integration_test.go) 同时验证不可变计划模板传递 Priority=7、资源键和递增许可序号;资源互斥不被优先级绕过。
|
||
|
||
升级回归继续验证 v4 安全策略、v9 应用配额及旧在途任务释放。v7 升级保留全部 325 个原有索引列定义,移除 70 个外键,既有任务内容不变;新增生成列对旧默认 0 任务等于原 available_at,新排序索引单独添加。模型/表列映射与完整零外键检查同步通过。
|
||
|
||
## 4. 公平领取与资源隔离证据
|
||
|
||
[领取池竞态用例](../../internal/dispatch/pool_test.go) 验证:
|
||
|
||
- 同应用 40 个繁忙队列连接请求,不能挤掉该应用另一个队列以及另一个应用的轮次。
|
||
- 全局在途数与单应用在途数不超上限;等待和执行中的请求共同受总容量约束,满额拒绝追加,不能把请求无限移入内部切片。
|
||
- 一个应用阻塞时其他应用仍进入领取;取消的待处理请求不执行、不占用后续轮次,释放容量并移除空组。
|
||
- 并发提交和关闭竞争时不再接新工作,取消在途操作,最终没有领取槽泄漏。
|
||
|
||
[空探测用例](../../internal/transport/httpserver/worker_dispatch_test.go) 验证 20 次同能力空探测只调用一次存储;能力集合顺序不影响身份,不同资源键支持声明分别探测;过期后再次访问数据库,缓存不超过 128 项,领取 panic 被隔离且不暴露原始异常。
|
||
|
||
[真实 MySQL/WSS 用例](../../internal/storage/mysql/fair_dispatch_integration_test.go) 使用一个节点、两个应用、13 条 Worker 连接以及仅 4 条连接的数据库池。显式持有繁忙应用的额度行锁,启用该应用 12 条消费连接,并通过 performance_schema.data_lock_waits 确认真实领取事务已经等待。保持锁未释放时,另一个应用成功更新容量并收到自身任务的持久化派发。随后才释放故障锁。
|
||
|
||
这个用例证明节点领取池不会因一个应用的多条连接等待额度而耗尽全部数据库连接。它没有把“领取隔离”扩大为所有管理/续租请求的资源隔离,也没有宣称跨节点完全相等吞吐或为新应用保留连接席位。
|
||
|
||
## 5. SDK 与进程恢复
|
||
|
||
[SDK 集成用例](../../sdk/go/priority_integration_test.go) 通过真实 HTTP 拒绝 -1、10、非整数、null、字符串和 bool;按 0、9、4 受理后,单槽自动 Worker 按 9、4、0 完成,任务查询和处理器看到相同冻结值。
|
||
|
||
进程用例在节点 A 保存三条不同优先级任务后强杀并 Wait 确认退出,启动不同 node_id 的 B,再接入 Worker。B 按 9、4、0 从数据库恢复领取,逐条 Start/Finish 成功,排序不依赖 A 的内存。
|
||
|
||
完整示例实际输出:
|
||
|
||
```text
|
||
submitted_priorities=0,9,4 executed_priorities=9,4,0
|
||
completed=3 attempts_per_task=1 worker_drained=true
|
||
```
|
||
|
||
原 Start/Finish ACK 丢失、Worker 强杀未知核查、计划强杀补跑、跨队列资源键恢复和应用额度持久化故障用例均在本次完整套件中通过。异步领取未使这些既有执行不变量失效。
|
||
|
||
## 6. 仍需验证的边界
|
||
|
||
- 调度池是节点内领取机会轮转。跨节点配额仍依赖共享 MySQL;连接分布不均时没有严格相同全局份额或全局权重。
|
||
- 每节点仍只接受 128 条 Worker 连接,没有每应用席位预留;请求级限流、恶意连接占用和全套资源隔离需要生产接入治理。
|
||
- 优先级不抢占已有执行,未来任务不会提前执行。暂停、额度不足、资源键占用、无兼容 Worker、旧积压和长任务都可能延长等待;排序优势不是时限承诺。
|
||
- 同能力空探测最多增加一个扫描周期的发现等待;满额、失去权限、节点重启或缓存丢失均不能授权执行。内存队列被丢弃后由重新扫描及持久化租约恢复。
|
||
- 生产还需验证 MySQL 索引选择、未来任务和高基数能力下的扫描成本、锁等待、混合负载公平性、队列/应用热点、数据库故障切换、RPO/RTO 和容量余量。迁移生成列和建索引需要目标规模维护窗口演练。
|