Files

161 lines
8.4 KiB
Markdown
Raw Permalink Normal View History

2026-08-11 13:32:21 +08:00
# 市场数据同步性能与完整性检查设计
## 1. 目标与边界
本设计保留现有“逐股获取六年 qfq 完整快照”的业务口径,通过 8 路受控并发和 PostgreSQL
连接/写入批量化缩短日常同步。另提供不访问 Tushare、不修改市场事实的手动完整性检查。
不引入按交易日增量下载、自动完整检查、自动修复或通用任务队列。选股仍只消费已完成且
覆盖率达标的市场同步批次。
## 2. 任务拆分与依赖
父任务只负责需求、跨子任务契约和最终集成,不直接实现产品代码。
1. `08-11-market-sync-concurrency-storage`
- 交付 8 worker、共享频控、连接池、批量 item 写入和集合覆盖率。
- 无子任务依赖,必须最先完成。
2. `08-11-market-integrity-check-api`
- 依赖第 1 项提供的池化 PostgreSQL 基础设施和 advisory lock 语义。
- 交付检查领域模型、迁移、只读比较、HTTP 触发和轮询。
3. `08-11-market-integrity-check-web`
- 依赖第 2 项冻结 HTTP 字段和状态值。
- 交付 `/sync` 页面、导航、触发、轮询和报告展示。
## 3. 同步执行模块
### 3.1 外部接口
`SyncMarketData.execute(command) -> SyncBatchSummary` 保持不变。并发数、请求协调器和仓储
由组合根注入,CLI 调用方不需要了解线程、频控或连接池细节。
### 3.2 单股 worker
把当前会修改共享 `failures` / `totals` 的 `_process_bar` 改为返回不可变
`SyncItemOutcome`:股票代码、状态、`WriteResult`、fingerprint 和可选安全错误。
每个 worker 内仍严格执行:
1. 获取该股票六年 qfq;
2. 读取旧正式 CSV 并比较重叠指纹;
3. 写临时 CSV;
4. 在独立 PostgreSQL 事务中幂等 upsert;
5. 数据库提交后原子发布 CSV;
6. 返回 outcome。
主线程通过 `as_completed` 聚合 outcome、更新进度和每 100 项批量写入同步审计。单股异常只
生成失败 outcome;未发布的临时文件必须清理。结果顺序不影响计数或最终状态。
### 3.3 请求协调器
请求协调器是 Tushare 适配器内部的深模块,接口只暴露“执行一个真实供应商调用”。实现隐藏:
- 正常请求保持最多 8 路并发,不用一个全局固定间隔把真实调用重新串行化;
- 保留现有可配置的单股请求间隔作为温和节流;
- “访问频繁/请稍后/超过频率/too many requests/429/403”等频控分类;
- 频控时共享 `cooldown_until`,默认按 60、120、180 秒增长并设置上限;
- 非频控临时错误沿用有界重试和普通退避;
- 注入 monotonic clock / wait 实现确定性单元测试;
- 日志只包含方法名、尝试次数和等待时间,不包含 token 或供应商原始响应。
为保留 Tushare qfq 计算,继续调用 `ts.pro_bar(api=coordinated_client, adj="qfq", ...)`。
`coordinated_client.daily` 与 `coordinated_client.adj_factor` 在原始异常仍可见的位置经过请求
协调器;`pro_bar` 内部重试降为一次,避免隐藏重试绕开全局频控。
### 3.4 PostgreSQL
- 增加 `psycopg_pool.ConnectionPool` 依赖,池最大连接数与 worker 数匹配并留出控制连接。
- CLI 用上下文管理器打开/关闭池;HTTP 进程使用按数据库 URL 缓存的池并在进程退出时关闭。
- worker 的 bar upsert 从池中借一个连接并保持单股票事务,不在线程间共享 connection。
- 主线程调用 `record_items(batch_id, outcomes)` 分批 upsert `market_sync_item`。
- 仓储新增 `count_valid_stocks(target_trade_date)`,用一条集合 SQL 计算 active 股票中同时存在
bar 和 daily_basic 的数量;删除同步编排对逐股 `has_*` 的依赖。
- advisory lock 的连接在整个批次期间保持借出,不和 worker 连接混用。
## 4. 完整性检查模块
### 4.1 语义
“完整性检查”验证 PostgreSQL 事实与已发布 CSV 快照是否一致,并验证 CSV 可以按现有领域规则
解析。它不判断 Tushare 是否已发布某日数据,也不能发现 PostgreSQL 和 CSV 同时缺少但供应商
实际存在的数据。
检查范围:
- 当前 active 股票主数据与 `stock-basic/current.csv`;
- 最近成功同步窗口内的每股 qfq bar 与 `bars/<ts_code>.csv`;
- 同一窗口内 PostgreSQL daily_basic 与 `daily-basic/<YYYY>/<YYYYMMDD>.csv`;
- 缺失、多余、无法解析、重复键、字段内容不一致和窗口越界。
### 4.2 独立持久化模型
新增表而不复用 `market_sync_batch`:
- `market_integrity_check`
- `id`, `status`, `window_start`, `window_end`
- `target_count`, `checked_count`, `issue_count`
- `error_type`, `error_message`, `created_at`, `finished_at`
- `market_integrity_issue`
- `check_id`, `item_kind`, `item_key`, `issue_type`, `message`, `created_at`
- 复合主键包含可稳定区分同一对象多个问题的序号或 issue key。
状态固定为:`running`、`passed`、`issues_found`、`failed`。`issues_found` 表示检查完整执行但数据
存在问题,不等于检查任务执行失败。
### 4.3 只读比较
`RunMarketIntegrityCheck` 是外部应用接口:
- `prepare() -> IntegrityCheckRun` 原子创建 running 记录;已有 running 时返回冲突。
- `execute(check_id) -> None` 获取与同步相同的 advisory lock,执行比较并收敛终态。
- `get(check_id, page, page_size)` 和 `get_latest(...)` 提供轮询读模型。
PostgreSQL adapter 使用有序 server-side cursor,按股票或交易日流式产生一组记录;CSV adapter
按同样 key 顺序读取。应用模块做 merge comparison,一次只保留一个股票或一个交易日的数据,
不把约 700 万行全量装入内存。
检查持有与同步相同的 advisory lock,保证 DB 与正式 CSV 在比较期间不会被同步修改。它只向
检查表写进度和问题;市场事实表和正式 CSV 不发生写入。
### 4.4 HTTP
路由归属 market_data presentation,并由 `/api/v1/market-data` 挂载:
- `POST /integrity-checks` → `202 Accepted`,返回 check id、status、窗口;
- `GET /integrity-checks/latest` → 最近一次检查或 `no_data`;
- `GET /integrity-checks/{check_id}?page=&page_size=` → 进度、终态和分页问题。
HTTP 使用仓库已有的 FastAPI `BackgroundTasks` 模式。后台任务出现未捕获异常时必须把检查记录
收敛为 `failed`。进程重启可能中断 in-process task;下一次触发允许显式把失去 advisory lock
且超过超时阈值的 running 记录标记为 failed,避免永久阻塞。该恢复只修改检查元数据。
## 5. Web 模块
启用现有“同步任务”导航并新增 `/sync` 路由,使用独立 `features/sync` 垂直切片:
- API 类型与函数;
- 最新检查 query、单次检查 polling query、触发 mutation;
- 页面展示检查说明、报告只读提示、最近窗口、进度、状态和分页问题;
- running 时禁用重复触发并按固定间隔轮询;终态停止轮询并刷新 latest;
- `409` 显示已有检查正在运行,`503` 显示存储不可用,其他错误保留可重试入口。
页面不把服务器状态复制到 Zustand。问题明细最少显示对象类型、key、问题类型和安全说明。
## 6. 兼容、部署与回滚
- 新迁移为 `0003_market_integrity_checks`,只新增检查表/索引,不修改事实表。
- 配置默认 worker 从当前未生效的 4 调整为 8;新增频控退避配置时同步 `.env.example`、
Compose 和市场数据运维文档。
- CLI 参数和退出码保持兼容;旧批次和旧 CSV 无需迁移。
- 回滚应用版本时新增检查表可暂留;数据库 downgrade 只删除检查表,不触碰市场事实。
- 若并发上线后供应商频控持续恶化,可通过环境变量把 worker 降为 1,无需回滚代码。
## 7. 验证策略
- 确定性并发测试证明 active worker 不超过配置值,结果汇总不依赖完成顺序。
- fake clock/condition 测试证明频控触发共享冷却,普通错误不冻结其他 worker。
- PostgreSQL 集成测试验证池化并发 upsert、批量 item、集合覆盖率和事务回滚。
- 完整性检查 fixture 覆盖 passed、缺 CSV、缺 DB、内容不一致、非法 CSV、检查冲突和批次异常。
- HTTP 测试锁定 202、409、分页与状态契约;Web 测试锁定触发、轮询停止、报告只读提示和错误态。
- 无网络 5002 股票调度基准锁定线程与仓储调用数量;生产约 30 分钟目标以部署日志验收。