Files
junhong_cmp_fiber/openspec/changes/archive/2026-04-16-fix-polling-config-multi-match/proposal.md
huang 5d9be1d7e4
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m27s
fix: 修正轮询配置多匹配逻辑,支持同卡匹配多个配置并按 priority 合并 interval
核心变更:
- MatchConfig 改为 MatchConfigs,返回所有匹配配置
- MergedTaskIntervals 按 task type 合并各配置,选取最高优先级(非 nil 且最小 priority 值)
- hasAnyEnabledInterval 过滤所有 interval 均为 NULL 的配置
- calcInitialDelay 重构为纯函数,接收 interval 参数
- 移除 getEnabledTaskTypes 和 getIntervalByTaskType(被 MergedTaskIntervals 替代)
- scheduler.go 新增心跳 key + 顶层 panic recovery + Init 完成守卫
- initializer.go 批量失败日志升级为 Error,逐条检查 Pipeline 命令错误
- 数据迁移:禁用 id=29 的轮询配置(所有 interval 均为 NULL)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 14:27:47 +08:00

57 lines
5.0 KiB
Markdown
Raw 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.
## Why
本次变更包含两个独立但相关的修复,均涉及轮询系统的核心可靠性:
**问题一(配置多匹配)**:当前 `PollingConfigManager.MatchConfig(card)` 的实现是"第一个匹配的配置直接返回,后续配置全部忽略"。这个设计在配置优先级体系下存在致命缺陷:**高 priority 的 catch-all 配置(如 id=29priority=1所有 interval 全为 NULL会匹配所有卡直接跳过更具体的配置如 id=28priority=25card_status_check_interval=600导致整个轮询系统失效**。
**问题二(调度器稳定性)**:轮询调度器(`Scheduler`)的主循环 `scheduleLoop` 没有顶层 panic recovery 和心跳上报机制。一旦 `processActivationTasks` 等路径发生 panic整个调度 goroutine 静默终止——所有分片队列持续积压,但 Worker 进程看起来正常,无任何告警。同时,初始化完成前调度器无谓地空轮询;`initBatch` 失败时仅记录 Warn 掩盖了卡初始化缺失的严重性Pipeline 执行只检查整体 error 而不逐条检查 cmder部分卡初始化失败会静默丢失。
本次修复将匹配逻辑从"独占式单匹配"改为"组合式多匹配",并同步修复上述调度器稳定性问题。
## What Changes
### 配置多匹配修复
- **修改 `MatchConfig``MatchConfigs`**:返回所有满足条件的配置(按 priority ASC 排序),而非第一个
- **修改 `matchConfigConditions`**:跳过所有 interval 均为 NULL 的配置(这些配置不应参与轮询匹配)
- **修改 `initBatch`**:对每种 task type从所有匹配配置中选取最高优先级的非 NULL interval
- **修改 `enqueueCard`**`lifecycle_service.go`):同上,合并多配置的 interval
- **修改 `getEnabledTaskTypes` → 移除**:不再需要,`MergedTaskIntervals` 已包含启用的 task type 列表
- **重构 `calcInitialDelay`**:修改函数签名接受 `interval int` 参数,移除对 `*model.PollingConfig` 的依赖(因为新逻辑中 interval 来源是 `MergedTaskIntervals` 合并结果,不再属于单一 config
- **修改 `requeueCard`**`internal/task/polling_base.go`):同上,使用 `MergedTaskIntervals` 替代 `MatchConfig` + `getIntervalByTaskType`
- **移除 `getIntervalByTaskType`**`internal/task/polling_utils.go``MergedTaskIntervals` 已提供按 task type 查 interval 的能力,该辅助函数不再需要
- **更新 `polling-config-manager` spec**:反映新的多匹配语义
### 调度器稳定性修复
- **新增 `scheduleLoop` 顶层 panic recovery**:主调度 goroutine 崩溃时记录 Error 日志,防止调度静默停止
- **新增 Scheduler 心跳机制**:每次 tick 写入 `polling:scheduler:heartbeat`TTL=2×ScheduleInterval供告警规则检测调度器存活
- **新增 Init 完成守卫**`processShardSchedule` 首先检查 `initializer.IsCompleted()`Init 未完成时跳过出队,消除启动期无效轮询噪音
- **`initBatch` 失败日志 Warn→Error**:批量初始化失败意味着部分卡未进入轮询队列,应使用 Error 级别
- **Pipeline 错误逐条检查**`pipe.Exec()` 后遍历 `[]redis.Cmder` 逐条检查 `cmd.Err()`,记录具体失败的 ZADD 命令,防止部分卡初始化失败静默丢失
- **新增 `RedisPollingSchedulerHeartbeatKey()`**`pkg/constants/redis.go`):心跳 Key 统一管理
## Capabilities
### Modified Capabilities
- `polling-config-manager`:原有的"第一个匹配独占"语义改为"所有匹配配置 + 按 task type 选取最高优先级 interval"。这是**需求级别变更**(不仅是实现细节),需要更新 spec 中的 MatchConfig 描述和 Scenario。
- `polling-scheduler`新增心跳上报、Init 完成守卫、顶层 panic recovery调度器可观测性和稳定性提升。
## Impact
- **受影响文件**
- `internal/polling/config_manager.go`:新增 `MatchConfigs``MergedTaskIntervals``hasAnyEnabledInterval`
- `internal/polling/initializer.go``initBatch` interval 选取逻辑;日志级别 Warn→ErrorPipeline 逐条错误检查
- `internal/polling/lifecycle_service.go``enqueueCard` interval 选取逻辑,移除 `getEnabledTaskTypes`
- `internal/polling/scheduler.go`:新增顶层 panic recovery、心跳写入、Init 守卫;新增 `initializer` 字段
- `internal/task/polling_base.go``requeueCard` interval 选取逻辑(**稳态路径,每次轮询任务完成后执行**
- `internal/task/polling_utils.go`:移除 `getIntervalByTaskType`(被 `MergedTaskIntervals` 替代)
- `pkg/constants/redis.go`:新增 `RedisPollingSchedulerHeartbeatKey()`
- **不受影响**API 接口、DB schema、Asynq 任务提交逻辑(`queue_manager.go`、handler 层
- **预期收益**
1. 配置系统按预期工作id=28suspended + card_status_check_interval=600能正确对停机卡生效
2. 调度器故障可被告警规则(检查心跳 key及时发现不再静默失效
3. 启动期不再产生无效轮询噪音
4. 卡初始化失败可见,不再静默丢失