All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m6s
线上保持现有纯 Asynq 架构,以公平孤儿扫描和提交后投递消除永久饥饿及事务可见性竞态。 Constraint: 线上保持现有纯 Asynq 架构,不引入 Outbox、迁移或新任务基础设施。 Rejected: 事务内投递或扩大扫描 LIMIT | 无法消除竞态和永久饥饿。 Confidence: high Scope-risk: narrow Directive: 后续分支整合时按目标分支的套餐接续架构独立处理,不混用本热修实现。 Tested: go build ./...(退出码 0);git diff --check;openspec validate fix-main-package-activation-starvation --strict。 Not-tested: 按用户要求未新增、修改或运行自动化测试;线上 SQL、查询计划和日志待部署后核验。
2.4 KiB
2.4 KiB
Why
功能 ID:hotfix-main-package-activation-recovery
线上已第二次出现原主套餐成功过期、队首待生效套餐仍长期停留在 status=0 的故障。只读诊断确认当前孤儿扫描固定取出的 100 条记录全部仍有占位套餐,真实孤儿永远无法进入恢复窗口;同时,过期事务提交前投递 Asynq 会让消费者读到旧套餐仍为生效中并把跳过误判为成功。
What Changes
- PostgreSQL 在
LIMIT 100前完成每个载体队首选择和status IN (1,2)占位排除,删除逐条检查的 N+1 查询。 - 旧主套餐过期和加油包失效事务提交成功后,才查询队首套餐并投递现有
package:queue:activationAsynq 任务。 - Redis 激活锁冲突返回现有套餐激活冲突错误,让 Asynq 按既有策略重试,不再确认假成功。
- Asynq Handler 仅在实际推进套餐或确认已完成幂等事实时记录成功;占位阻塞、等待实名和锁冲突记录明确原因。
- 不新增 Outbox、数据库迁移、依赖或新任务类型,保持线上现有纯 Asynq 架构。
- 按用户明确要求,不新增、修改或运行自动化测试;使用全量构建、只读 SQL、查询计划和日志核验。
Capabilities
New Capabilities
无。
Modified Capabilities
package-queue-activation:补充纯 Asynq 过期接续的提交后投递、真实孤儿公平扫描、锁冲突重试和准确成功语义。
Impact
- 适用范围:仅当前
main线上代码。 - 架构通道:旧套餐轮询复杂写用例,沿用
Polling Handler → GORM transaction → Asynq → Package Activation Service;不迁移未触碰的套餐模块。 - 代码:
internal/polling/package_activation_handler.go、internal/service/package/activation_service.go。 - 数据库:无迁移;仅调整
tb_package_usage查询顺序与过滤。 - API/前端:无改动。
- 依赖:继续使用 GORM、PostgreSQL、Redis、Asynq 和 Zap,不新增依赖。
- 性能:孤儿扫描由最多 101 次查询收敛为一次候选查询和有限任务投递;使用查询计划确认数据库耗时。
- 审计与可靠性:Audit Event、Domain Ledger、Integration Log、Outbox 均为 N/A;
tb_package_usage是权威状态,现有 Asynq + 周期孤儿扫描负责最终恢复。 - 验证:不写自动化测试;执行
gofmt、静态检查、go build ./...、只读 SQL及日志核验。