Files
junhong_cmp_fiber/openspec/changes/add-polling-async-ingest/proposal.md
邹进 1214181e78
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 16m2s
feat: 轮询观测入库异步化,调网关与入库分离,采集记录经消息攒批写入
- 轮询步骤异步模式下只调网关并发布 card.ingest 入库消息,入库消费者固定并发 30、按采集 ID 去重、可重试错误不限次数退避重投
- 采集记录经 card.collection 攒批写入,失败整批重投,新增 (observation_id, observed_at) 唯一索引
- 网络、实名观测增加乱序保护;轮询读查询走缓存,套餐状态查询各自计时且失败不缓存
- 入库积压告警与反压;系统配置 polling.ingest.mode 默认 sync 可回退;测试环境 nsqd 消息全部落盘

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:33:29 +09:00

40 lines
4.1 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
测试环境 worker 每小时约 8,000 次 SQL `context deadline exceeded`、13.8 万条慢查询告警,采集记录因 3 秒写超时持续丢失。根因是按运营商账户调度的 NSQ 消费者只有"每账户上限"(41 个账户 × 20 = 820),没有总量上限,而每个轮询步骤在同一个任务里先调网关再同步写库,三百多个任务争抢 60 个数据库连接。依据 `docs/2026-11-06-轮询入库异步化技术方案.md`,把"调上游"与"入库"拆开:调网关继续按账户并发,入库单独发消息、由固定并发的消费者处理。
## What Changes
- 轮询的卡状态、流量、实名步骤只做"读缓存 → 调网关 → 发入库消息",不在卡任务内写库;步骤结果只反映网关调用是否成功。
- 新增 NSQ topic `card.ingest` 与入库消费者,并发固定(默认 30),与账户数量无关;入库复用现有 `ApplyTrafficObservation`、`ApplyNetworkObservation`、`ApplyCardObservation`。
- 入库可重试错误按退避不限次数重试、不丢弃;业务性错误记 WARN 后丢弃;本期不做死信。
- 入库幂等与顺序:按 `observation_id` 去重;采集队列按采集时间去重;网络、实名观测"更新的才生效";采集记录唯一索引。
- 新增 NSQ topic `card.collection` 与批量消费者,攒批写采集成功表、失败表;删除采集记录 3 秒写超时与"写失败只记告警"。
- 优先轮询项完成、对接日志"业务事实发生变化"、网络观测导致的停止轮询、套餐步骤跳过标记移到入库段。
- 轮询步骤调网关前的读查询走缓存(卡信息、优先项、运营商开关、绑定关系);配置匹配查套餐状态每次查询单独计时,失败不缓存"未知"。
- 入库积压监控:积压超过 20 万条告警,超过 50 万条调度器暂停派发新卡任务,降到一半以下恢复。
- 系统配置 `polling.ingest.mode`(`async` / `sync`)一键回退;发消息失败时降级为同步入库。
- 测试环境 nsqd 改为 `--mem-queue-size=0`,消息全部落盘。
- 后台人工刷新、运营商回调、激活前结算、观测序列保持同步入库。
- 不在本 Change:死信表与重放、生产环境发布、按账户调度参数调整。
## Capabilities
### New Capabilities
- `polling-async-ingest`:轮询观测入库消息、入库消费者并发与重试、幂等与顺序、采集记录批量写、积压告警与反压、同步回退与降级。
### Modified Capabilities
- `carrier-polling-scheduler`:卡任务的检查步骤改为只调网关并发入库消息;套餐步骤跳过依据改为入库段写的流量评估标记。
- `card-traffic-metering`:采集记录写入改为经消息批量写、失败重试不丢,替代"写入失败只记告警"。
## Impact
- 前置:`add-carrier-polling-scheduler`、`add-card-traffic-queue-and-observed-deduction`、`refactor-audit-log-persistence` 须先归档(本 Change 修改它们新增或修改的要求)。
- 代码:`internal/task/polling_carddata_handler.go`、`polling_cardstatus_handler.go`、`polling_realname_handler.go`、`carrier_card_executor.go`、`polling_priority_claim.go`;`internal/application/cardobservation/`(`traffic.go`、`traffic_collection.go`、`network.go`、`apply.go`);`internal/polling/config_manager.go`、`tier.go`;采集队列 Lua;新增入库消费者、采集记录批量消费者与积压监控;`cmd/worker/carrier_scheduler.go`、`internal/polling/carrierqueue/dispatcher.go`(反压)。
- 数据库:采集成功表、失败表新增 `(observation_id, observed_at)` 唯一索引(成对迁移)。
- 配置:系统配置 `polling.ingest.mode`、`polling.ingest.alert_depth`、`polling.ingest.pause_depth`;启动配置 `polling.ingest.concurrency`、`polling.ingest.timeout`、`polling.collection.batch_size`。
- Redis:`ingest:done:<observation_id>`(TTL 48h)、`flow_evaluated:<card_id>`、有活动优先项的卡集合。
- 部署:`docker-compose.prod.yml` 中 nsqd 改 `--mem-queue-size=0`。
- 范围:基于 `Iteration/8-11`,只部署测试环境。