## 1. 幂等与乱序基础 - [x] 1.1 采集队列 Lua 按 `observed_at` 去重(现有 Lua 已按同一采集时间去重,无需改动) - [x] 1.2 `ApplyNetworkObservation` 增加采集时间早于 `last_card_status_check_at` 时不改状态的保护 - [x] 1.3 `ApplyCardObservation` 增加采集时间早于 `last_real_name_check_at` 时不改状态的保护 - [x] 1.4 迁移:采集成功表、失败表新增 `(observation_id, observed_at)` 唯一索引(成对 up/down) ## 2. 配置 - [x] 2.1 启动配置 `polling.ingest.concurrency`(30)、`polling.ingest.timeout`(30s)、`polling.collection.batch_size`(200),含默认值、环境变量与校验 - [x] 2.2 系统配置 `polling.ingest.mode`(async/sync)、`polling.ingest.alert_depth`(200000)、`polling.ingest.pause_depth`(500000,须大于告警阈值) - [x] 2.3 启动连接预算检查:入库并发 + 1 > `MAX_OPEN_CONNS` − 20 时记 ERROR ## 3. 采集记录批量写 - [x] 3.1 定义 `card.collection` 消息与发布器(成功、失败两种记录) - [x] 3.2 批量消费者:缓冲按 200 条或 1 秒刷写,多行 INSERT `ON CONFLICT DO NOTHING`,成功统一 FIN、失败统一重投 - [x] 3.3 `ApplyTrafficObservation` 采集记录改为发布消息,发布失败同步写;删除 `collectionRecordTimeout` 与"写失败只记告警" - [ ] 3.4 测试环境验证:攒批、整批失败重投、重复记录忽略(项目自动化测试为 N/A,按 ENG-TEST-001 在测试环境验证) ## 4. 入库消息与消费者 - [x] 4.1 定义 `card.ingest` 消息(三种观测、原始数据、优先项、发布时间)与编解码,补测试 - [x] 4.2 入库消费者:已处理标记检查、按类型调用 `Apply*Observation`、成功写标记(48h) - [x] 4.3 错误分类与重投:可重试错误按 5s/15s/30s/1m/2m/5m 延迟重投、不限次数,第 10 次起记 ERROR;业务性错误记 WARN 后 FIN - [x] 4.4 收尾归属:优先执行保持同步入库(优先项完成、对接日志沿用原逻辑);`StopPolling` 由步骤按网关结果移出调度;入库段写 `flow_evaluated:` - [x] 4.5 在 `cmd/worker/carrier_scheduler.go` 注册入库与采集记录消费者(`MaxAttempts=0`),随 worker 优雅退出 - [ ] 4.6 测试环境验证:重复投递、提交后未写标记重放、卡不存在、可重试错误重投 ## 5. 轮询步骤改造 - [x] 5.1 流量步骤:异步模式下网关成功后发布入库消息即成功返回;网关失败直接发布采集失败记录 - [x] 5.2 卡状态步骤、实名步骤:异步模式下发布入库消息 - [x] 5.3 发布失败降级为同步入库并记 WARN;`sync` 模式走原逻辑 - [x] 5.4 套餐步骤跳过依据改为读 `flow_evaluated:` - [ ] 5.5 测试环境验证:异步成功、发布失败降级、同步模式行为不变、套餐步骤跳过 ## 6. 读查询缓存 - [x] 6.1 步骤读卡改为读卡缓存(去掉每步先清缓存再查库);入库段提交后失效缓存 - [x] 6.2 有活动优先项的卡集合:入队加、终态删、启动全量重建;步骤先查集合 - [x] 6.3 运营商实名开关、重置日进程内缓存 1 分钟;设备轮询开关与绑定关系并入卡缓存 - [x] 6.4 `lazyTierFacts` 每次查询单独计时;`Prefill` 失败的卡不写缓存 ## 7. 积压监控与反压 - [x] 7.1 leader 每分钟读 nsqd stats,记录 `card.ingest`、`card.collection` 积压与最老等待时间 - [x] 7.2 超过告警阈值或等待 > 10 分钟记 ERROR - [x] 7.3 超过反压阈值置暂停标记,分发器暂停领取新卡;低于一半恢复 ## 8. 部署(测试环境) - [x] 8.1 `docker-compose.prod.yml` nsqd 参数改 `--mem-queue-size=0` - [ ] 8.2 `go build ./...`、相关包测试、OpenSpec `--strict` 校验通过 - [ ] 8.3 提交并推送 `Iteration/8-11`(自动部署测试环境),`polling.ingest.mode` 先保持 `sync` - [ ] 8.4 测试库执行唯一索引迁移(用户授权后) ## 9. 测试环境验证 - [ ] 9.1 切 `async` 后 1 小时:`context deadline exceeded` 从每小时约 8,000 次降到个位数 - [ ] 9.2 慢查询告警与 worker 日志量明显下降 - [ ] 9.3 采集记录对账:入库消息数 = 成功表 + 失败表新增行数 - [ ] 9.4 抽样卡日快照、扣减与同步模式一致;重投与乱序不重复扣减 - [ ] 9.5 `card.ingest` 常态等待 < 30 秒 - [ ] 9.6 切回 `sync` 行为恢复;停 nsqd 时步骤降级同步入库 - [ ] 9.7 nsqd 重启后未处理消息继续消费 ## 10. 归档 - [ ] 10.1 `add-carrier-polling-scheduler`、`add-card-traffic-queue-and-observed-deduction`、`refactor-audit-log-persistence` 归档后再归档本 Change