Files
orbit/docs/reports/phase-3-keyed-concurrency.md

85 lines
7.5 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 资源键许可与过时执行隔离回归
验证日期:2026-09-17。交付 [资源键互斥接口、SDK 和场景手册](../api/keyed-concurrency.md)。P3 优先级/公平性、可靠结果订阅及 P4–P7 继续推进,持续目标尚未完成。
## 1. 已实现内容
- 显式 concurrency_key 在命名空间内跨队列共享一个有效尝试许可;原 business_key 保持追踪含义,不改变既有任务并发行为。
- 领取事务同时维护应用额度、队列许可、键许可、任务和尝试。资源占用时不消耗新的尝试;候选 LIMIT 前过滤忙碌键。
- concurrency_token 使用持久化键行的单调版本,跨任务递增、允许间隔、拒绝溢出;同尝试重连保留。资源端必须原子比较并按稳定操作身份幂等处理实际写入。
- 续租、取消、结果、退避及到期恢复同步维护两类许可。UNKNOWN 释放执行许可并保留核查与未知效果,旧结果不能释放后续尝试的许可。
- Worker 显式协商 concurrency_keys;旧 Worker 不消费有键任务,自动 SDK 默认开启。协议 Schema、SDK 精确 uint64、尝试历史与计划模板同步支持。
- [000011 迁移](../../internal/storage/mysql/migrations/000011_keyed_concurrency.up.sql) 新增四个中文注释字段,无外键。当前 33 个实体、273 个直接声明的持久化字段均有中文注释,SQL 同步 COMMENT。
- [完整示例](../../sdk/go/examples/keyed-concurrency/main.go) 创建两个队列、两个 Worker、相同资源键,核对序号并模拟资源端拒绝旧写入。
## 2. 环境与命令
macOS 本机、Go 1.27.1、Docker MySQL 8.4.10;服务和数据库仅绑定回环,随机私密凭据及临时数据库随测试清理。没有修改既有业务数据库。
```sh
make fmt test vet build
make integration
cd sdk/go
GOWORK=off go test -race ./...
```
基础竞态测试、vet、三个 CLI 构建及独立 SDK module 验证通过。无集成环境的普通 go test 会跳过真实服务用例,实际集成证据来自 scripts/test-mysql.sh。
本次完整集成结果:
| 分组 | 结果 |
| --- | --- |
| 全部真实 MySQL 集成 | PASS,71.436 秒 |
| 资源键七组数据库场景 | PASS,4.36 秒 |
| v7 无外键升级 | PASS,3.43 秒,70 → 0,325 个既有索引列定义保留 |
| v9 应用额度回填及当前迁移 | PASS,3.81 秒 |
| 历史分页、权限、精确结果和证据 | PASS,0.48 秒 |
| SDK Worker 连接及运行器 | PASS,82.224 秒 |
| 其中资源键、四任务、双队列及 Start ACK 丢失 | PASS,3.32 秒 |
| SDK 计划 / 队列资源 / 应用资源 | PASS,分别 5.634 / 4.697 / 1.372 秒(含包开销) |
| 六项真实进程故障组 | PASS,25.173 秒 |
| 其中资源键跨节点恢复 | PASS,2.27 秒 |
| 十一个完整示例 | 全部通过 |
示例包括 health、tasks、worker-protocol、worker、history、retry、schedules、cancellation、resources、application-resources 和 keyed-concurrency。上述时长是回归执行时间,不代表吞吐或生产容量。
完整套件后进一步移除了空资源键判断对数据库文本排序规则的依赖,并补充仅含组合符和零宽字符的非空键回归。最终定向审计再次通过资源键八组场景、v7 零外键及索引/数据保留、v9 配额迁移和 v4 安全重试升级,总包耗时 21.104 秒。新旧普通任务、计划模板、特殊 UTF-8 键和回滚路径均在这些真实数据库用例中验证。
## 3. 数据库与协议证据
[数据库用例](../../internal/storage/mysql/keyed_concurrency_integration_test.go) 验证:
1. 老 Worker 对有键任务不创建尝试;相同 business_key 的无键任务仍可并行;显式启用后获得许可,原进程更改支持声明被拒绝,旧 false 注册摘要仍可重连。
2. 两个节点、两个队列竞争同一键仅一个领取;队列和键各有一条许可,但应用只计一个尝试。未开始取消后另一个队列拿到更大序号;Start 重报、续租、尝试详情和完成后计数正确。
3. 前面 20 个同键阻塞任务不遮挡后面的其他资源;阻塞项尝试次数为零。同应用另一个命名空间的相同键独立执行。
4. 明确失败退避和 UNKNOWN 正确释放执行许可;待核查仍占积压。旧迟到结果仅保存证据,不能释放另一个新任务的许可;未开始过期同样回收。
5. 领取事件写入失败时,尝试、键行、两类许可和应用计数一起回滚。大小写、组合及分解字符按精确字节区分;仅含组合符或零宽字符的非空键不被文本排序规则折叠为空。
6. 接近 uint64 上限时仍完整保存序号,最后一次释放可成功;后续授权返回冲突且不消耗尝试,不回绕。
7. 不可变计划模板传递资源键;连续触发同键任务按许可逐个执行。非法首尾空白、控制字符被拒绝。
[协议契约测试](../../internal/transport/httpserver/worker_contract_test.go) 同步检查 hello 扩展、带键派发、超过浮点精度的序号,以及缺键、缺序号、数值型或零序号。 [SDK 线协议测试](../../sdk/go/keyed_concurrency_test.go) 使用真实 TLS WebSocket 检查未协商支持、非法键、字段配对和大序号精度。
本阶段修复了尝试详情增加序号后 SELECT/Scan 字段数量不匹配。完整回归还暴露历史查询夹具假设单次全库恢复扫描就能处理指定任务;其他用例留下更早的到期任务后,该假设不成立。用例改为有界分批恢复,仍要求原任务进入 WAITING、第二次尝试成功、分页和证据全部一致,没有降低业务断言。
## 4. SDK 与真实故障
[SDK 集成用例](../../sdk/go/keyed_concurrency_integration_test.go) 通过真实服务运行四条同键任务,分布在两个队列,各 Worker 容量为 4。处理器同资源活跃数始终为 1;Start 已提交但 ACK 丢失后自动重连,处理器总共执行四次,每条任务只有一次尝试。处理器取得的序号与列表及详情中的序号完全一致。
真实进程测试强杀并 Wait 确认节点 A 退出,再启动不同 node_id 的 B。原 Worker 身份 Resume 原 RUNNING 尝试;另一队列不能领取同键任务,尝试数保持零。原任务完成后另一队列得到更大序号;旧 Finish 重放不破坏新尝试,后者仍可续租和正常完成。
完整示例实际输出:
```text
queues=2 completed=2 tokens_increasing=true
resource_writes=2 stale_write_rejected=true worker_drained=true
```
## 5. 当前边界
- 只保证同一命名空间、显式相同资源键的有效许可互斥。省略键或另一个命名空间不参与;不承诺提交顺序或跨队列公平性。
- 超时释放不会物理中断旧外部请求;UNKNOWN 不自动冻结整个资源。外部系统须实现原子序号校验、操作幂等和必要的业务冻结。仅有最高已见序号不能拒绝尚未见到更高序号时的所有旧写入。
- 序号不是认证凭据或执行授权。处理器必须经当前 Start/Resume 授权;历史查询和 task.assign 不能直接授权业务执行。
- 一个 MySQL 主库与应用额度锁是当前一致性边界。键行不能随任务清理而重置;高基数键增长、锁等待、吞吐和恢复延迟仍需 P6 负载验证。
- 停止旧写入节点后迁移,再启新服务和新 SDK;混合旧写入节点、数据库复制切换、跨故障域网络分区、备份恢复和实际 RPO/RTO 尚未验收。
- 示例资源模型使用进程内内存,仅用于清晰展示校验位置,不代表生产资源存储实现。Weave、可靠订阅及整个平台生产验收仍在开发计划中。