无限拉取死循环
朴素增量逻辑 if latest > last 遇上「永远没有新数据」的标的,会把每日任务拖成一场无声的无限重试。#19 事故档案。
层级 ODS · 数据接入 严重度 高 关联 #19 · commit 8c493ff · data_loader.py database.py
朴素的增量同步逻辑 if latest > last,在遇到无数据可拉取的标的时,会导致每日增量任务陷入无限重试。
现象
MMC、CTRA 等标的在 IEX 免费源无数据覆盖。每次任务从旧时间戳出发、尝试补齐至当日,返回空数据后下次重复同一操作——CPU 资源与 Alpaca API 配额被持续消耗,任务耗时逐日递增,但无任何异常抛出。
定位
顺着「为什么 pipeline 越来越慢」往回查,发现少数几只标的每天都在重复拉取同一段空区间。问题出在增量判定那一行:
# 旧逻辑:只要落后于今天,就无脑补
if latest_date > last_synced:
fetch(from=last_synced) # 空源标的永远满足 → ∞latest_date(交易日历的今天)永远大于 last_synced(这只标的最后一次有数据的日子),条件恒为真。
根因
增量同步的可靠性取决于异常路径的处理。「数据源不覆盖该标的」与「数据尚未到达」是两种本质不同的状态,朴素逻辑未加区分——将「标的已退市 / 数据源不覆盖」误判为「仍有待补齐的增量」。
修复
新增 sync_status 水位线表(src/utils/database.py),为每个 (symbol, market, source, interval) 记录:
last_checked_date—— 上次「检查」到哪天(不是上次「有数据」到哪天)consecutive_empty_days—— 连续多少天拉回空数据status——active/ 停用
data_loader.py 拉取前先读水位线:已停用的直接跳过;连续空数据达到 CONSECUTIVE_EMPTY_WARN_THRESHOLD 告警、达到 CONSECUTIVE_EMPTY_SUSPEND_THRESHOLD 则标记停用。一旦真的有新数据,consecutive_empty_days 归零。
教训:静默失败比显式异常代价更高。增量同步的难点不在于正常的数据拉取,而在于对「无数据可拉取」状态的正确识别与处理。
关联
- 手册:1. 数据接入层 ODS
- 同源教训:周频换手把摩擦放大数倍——同样是「没报错但持续烧钱」的一类。