Develop #11
+2
-2
@@ -3,7 +3,7 @@
|
||||
"name": "selection-results-filter-state-ui",
|
||||
"title": "迭代选股结果页筛选与状态入口",
|
||||
"description": "",
|
||||
"status": "in_progress",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
@@ -11,7 +11,7 @@
|
||||
"creator": "yuxuanhui",
|
||||
"assignee": "yuxuanhui",
|
||||
"createdAt": "2026-08-10",
|
||||
"completedAt": null,
|
||||
"completedAt": "2026-08-11",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
@@ -0,0 +1,6 @@
|
||||
{"file":".trellis/spec/backend/market-data-sync.md","reason":"检查并发、完整性审计和现有同步契约的集成一致性。"}
|
||||
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"检查后端 lint、类型、测试、迁移和集成证据。"}
|
||||
{"file":".trellis/spec/frontend/quality-guidelines.md","reason":"检查前端格式、lint、类型、测试和构建证据。"}
|
||||
{"file":"docs/adr/0003-postgresql-as-market-data-store.md","reason":"检查完整性报告没有改变 PostgreSQL 事实源地位。"}
|
||||
{"file":"docs/adr/0004-tushare-six-year-snapshot-sync.md","reason":"检查并发仍保持事务/CSV 发布、失败隔离和滚动边界。"}
|
||||
{"file":".trellis/tasks/08-11-market-data-sync-performance-audit/prd.md","reason":"执行父任务最终 acceptance criteria 对照。"}
|
||||
@@ -0,0 +1,160 @@
|
||||
# 市场数据同步性能与完整性检查设计
|
||||
|
||||
## 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 分钟目标以部署日志验收。
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{"file":".trellis/spec/backend/market-data-sync.md","reason":"父任务集成时保持 qfq、事务/CSV 顺序、覆盖率和 CLI 契约。"}
|
||||
{"file":".trellis/spec/backend/http-api-contracts.md","reason":"集成完整性检查 HTTP 与 Web 同源契约。"}
|
||||
{"file":".trellis/spec/frontend/hook-guidelines.md","reason":"集成页面触发、轮询和终态停止行为。"}
|
||||
{"file":"docs/adr/0004-tushare-six-year-snapshot-sync.md","reason":"检查三个子任务没有绕过六年快照和失败隔离决策。"}
|
||||
{"file":".trellis/tasks/08-11-market-data-sync-performance-audit/research/legacy-sync-and-current-bottlenecks.md","reason":"提供生产基线、旧项目证据与已确认技术选择。"}
|
||||
{"file":".trellis/tasks/08-11-market-data-sync-performance-audit/design.md","reason":"提供任务依赖、跨层接口、状态和回滚设计。"}
|
||||
@@ -0,0 +1,39 @@
|
||||
# 父任务执行计划
|
||||
|
||||
父任务不直接修改产品代码;按以下顺序启动、检查并集成子任务。
|
||||
|
||||
## 1. 子任务顺序
|
||||
|
||||
1. 完成 `08-11-market-sync-concurrency-storage`。
|
||||
- 先锁定并发、频控、数据库池和批量接口。
|
||||
- 完成后运行后端全量质量门禁和无网络基准。
|
||||
2. 完成 `08-11-market-integrity-check-api`。
|
||||
- 以前一子任务的连接池与 advisory lock 契约为依赖。
|
||||
- 完成迁移、只读检查、后台任务和 HTTP 契约后运行后端全量门禁。
|
||||
3. 完成 `08-11-market-integrity-check-web`。
|
||||
- 只消费已冻结的 HTTP 模型和状态值。
|
||||
- 完成导航、路由、触发/轮询和问题展示后运行前端全量门禁。
|
||||
|
||||
## 2. 集成检查
|
||||
|
||||
- 检查三个子任务的字段、状态和路径完全一致。
|
||||
- 模拟同步持锁时触发检查,以及检查持锁时启动同步,确认不会并发修改/读取快照。
|
||||
- 确认检查批次不影响首页最近同步、selection 数据源选择或 CLI 退出码。
|
||||
- 确认运行检查时不会构造或调用 Tushare adapter。
|
||||
- 运行迁移 upgrade/downgrade(测试数据库可用时)并核对 schema metadata。
|
||||
- 运行根目录 `./dev.sh check` 与 `./dev.sh test`。
|
||||
- 检查四种 Compose config。
|
||||
|
||||
## 3. 生产验收与回退
|
||||
|
||||
- 首次部署先执行迁移,再用少量股票/测试环境验证 8 worker 和共享频控日志。
|
||||
- 生产正常批次记录总耗时、频控次数、累计冷却时间、失败数和覆盖率;目标约 30 分钟。
|
||||
- 如频控导致失败率上升,通过 `ZHIXING_MARKET_DATA_MAX_WORKERS` 降低并发。
|
||||
- 完整性检查先在非交易时段触发;确认只产生检查表写入,不改变事实表行数或 CSV mtime。
|
||||
|
||||
## 4. 完成门禁
|
||||
|
||||
- 三个子任务 acceptance criteria 全部有实际验证证据。
|
||||
- 父 PRD 中所有 acceptance criteria 可映射到子任务测试或生产验收项。
|
||||
- 完成 `trellis-check` 后再评估是否有经用户批准、值得提升到 `.trellis/spec/` 的规则。
|
||||
- 不自动 commit、push、archive;这些动作仍需用户明确授权。
|
||||
@@ -0,0 +1,84 @@
|
||||
# 优化市场数据同步并增加完整校验入口
|
||||
|
||||
## Goal
|
||||
|
||||
把当前接近四小时的市场数据同步缩短到可接受范围,同时保留六年 qfq
|
||||
历史修订检查、单股票失败隔离、PostgreSQL/CSV 发布一致性和选股覆盖率契约。
|
||||
日常同步由外部调度器执行;耗时较长的完整检查改为用户在 Web 页面中主动触发并查看进度与结果。
|
||||
|
||||
## Background
|
||||
|
||||
- 2026-08-10 生产批次处理 5002 只股票,总耗时 13339.3 秒;行情阶段后半段稳定在
|
||||
2.525–2.820 秒/只,属于逐股固定成本。
|
||||
- 当前实现串行调用 5002 次 `pro_bar(..., adj="qfq")`。本机 Tushare 1.4.29
|
||||
源码表明每次 qfq `pro_bar` 至少调用一次 `daily` 和一次 `adj_factor`。
|
||||
- `Settings.market_data_max_workers` 当前默认 4,但未被同步用例或 CLI 使用。
|
||||
- 旧项目 `../zgnb-project` 使用 `ThreadPoolExecutor(max_workers=8)` 逐股拉取六年
|
||||
`pro_bar`;识别频控错误后按 60/120/180 秒退避,普通错误按 5/10/15 秒退避。
|
||||
它是八路逐股并发,不是按交易日一次请求全市场。
|
||||
- 当前 PostgreSQL 适配器在单股 upsert、单股审计记录和覆盖率存在性检查中频繁创建
|
||||
新连接;bar 阶段完成后仍耗时约 110.2 秒才完成批次。
|
||||
- 当前 Web 导航已有未启用的“同步任务”入口,后端已有 selection 的
|
||||
`202 Accepted + BackgroundTasks + polling` 长任务模式,但生产服务重启会中断进程内任务。
|
||||
|
||||
## Requirements
|
||||
|
||||
### R1. 八路受控并发
|
||||
|
||||
- 需要同步单股票行情时默认使用 8 个 worker,并允许通过
|
||||
`ZHIXING_MARKET_DATA_MAX_WORKERS` 配置。
|
||||
- worker 必须保留单股票事务、临时 CSV、数据库提交后原子发布和单股票失败隔离语义。
|
||||
- Tushare 频控由所有 worker 协同处理,不能让一个 worker 进入长退避时其他 worker
|
||||
继续无界冲击接口;重试次数、退避和最终失败必须可观测且不泄露凭据。
|
||||
- 同一环境仍只允许一个市场数据同步或完整检查批次持有 advisory lock。
|
||||
|
||||
### R2. 日常同步与完整检查分离
|
||||
|
||||
- 外部调度器继续触发日常同步,日常同步不依赖浏览器或 FastAPI 生命周期定时器。
|
||||
- 日常同步继续按旧项目的数据口径,对全部目标股票逐股获取六年 qfq 完整快照;性能优化来自
|
||||
8 路受控并发,而不是改成按目标交易日增量拉取。
|
||||
- 本任务不引入 `adj_factor` 持久化或按复权因子变化选择性重建历史的增量方案。
|
||||
- PostgreSQL/CSV 完整性检查不由 cron 自动触发,只允许用户从 HTTP/Web 主动发起;该检查
|
||||
不调用 Tushare,也不重新下载行情。
|
||||
- 完整检查需要持久化批次、目标/已检查/问题计数和安全错误,并提供轮询查询契约。
|
||||
- 用户可在“同步任务”页面触发完整检查;运行中禁止重复触发,并持续展示进度和最终结果。
|
||||
- 完整检查只报告问题:除检查批次和问题明细外,不修改股票主数据、行情、估值或 CSV;发现的
|
||||
异常由后续日常同步修复。
|
||||
|
||||
### R3. PostgreSQL 批量化
|
||||
|
||||
- 同一个批次复用有限数量的数据库连接,避免每只股票为 upsert、审计记录和覆盖率检查
|
||||
分别建立新连接。
|
||||
- 同步 item 审计记录支持批量写入,同时保留失败股票的可定位记录。
|
||||
- 覆盖率通过集合 SQL 一次计算,不逐股票执行 `has_bar` / `has_daily_basic`。
|
||||
- 并发写入不得破坏 `(ts_code, trade_date)` 幂等约束或批次汇总计数。
|
||||
|
||||
### R4. 兼容性
|
||||
|
||||
- 保留现有 CLI 成功/部分成功/失败退出码、覆盖率阈值和失败重试语义。
|
||||
- 保留六年滚动窗口、qfq 唯一口径、PostgreSQL 事实源和 CSV 恢复快照边界。
|
||||
- 日常同步结果仍能作为选股批次的数据来源,完整检查不得让正在使用的最近成功批次
|
||||
短暂变成不可用。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] 默认并发数为 8,配置覆盖有效;并发测试能证明最多只有配置数量的单股任务同时执行。
|
||||
- [ ] 模拟 Tushare 频控时,所有 worker 服从共享冷却窗口,随后成功恢复或在重试预算耗尽后记录失败。
|
||||
- [ ] 单股票失败不发布其临时 CSV,不回滚其他成功股票,重复执行保持幂等。
|
||||
- [ ] 日常同步与手动完整检查的职责符合最终确认的数据口径。
|
||||
- [ ] `POST` 完整检查返回可轮询的批次标识;重复触发返回冲突;查询端点展示进度、状态和错误。
|
||||
- [ ] “同步任务”页面可触发完整检查并展示无记录、运行、通过、发现问题、执行失败状态。
|
||||
- [ ] 完整检查不调用 Tushare,不修改市场事实表或正式 CSV;有问题时只持久化安全报告。
|
||||
- [ ] 覆盖率计算不再产生逐股票查询;审计记录使用批量写入;连接创建数量不随股票数线性增长。
|
||||
- [ ] 5002 只股票的无网络性能测试/受控基准锁定并发调度和数据库调用数量;生产目标为正常频控条件下
|
||||
完整下载约 30 分钟,结果需通过部署后日志验证,不能用本地 mock 代替生产结论。
|
||||
- [ ] 后端 Ruff、Pyright、pytest,前端格式、lint、typecheck、Vitest 和构建全部通过。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 不新增分钟级或实时行情。
|
||||
- 不改变选股公式、覆盖率阈值业务含义或六年保留边界。
|
||||
- 不自动安排周期性完整检查。
|
||||
- 不把日常同步改造成按交易日/复权因子增量拉取。
|
||||
- 不在完整检查中自动修复、重试或重新下载异常对象。
|
||||
- 不在本任务中引入与市场数据无关的通用任务平台。
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# 旧项目并发模式与当前性能证据
|
||||
|
||||
## 生产基线
|
||||
|
||||
- 批次 `7ca4a247-ddb8-4038-a19f-856421433a6a` 在 2026-08-10 处理 5002 只股票,
|
||||
总耗时 13339.3 秒。
|
||||
- 从 1800 到 5002 的日志计算得到平均 2.674 秒/只,各区段 2.525–2.820 秒/只,
|
||||
说明主要成本随股票数线性增长。
|
||||
- bar 阶段完成到批次完成还有 110.2 秒;当前代码在此期间按股票执行两次存在性查询。
|
||||
- 4 worker 的理想线性下限约 55.1 分钟,8 worker 的理想线性下限约 27.6 分钟。
|
||||
|
||||
## 当前实现
|
||||
|
||||
- `application/sync.py:237` 使用普通 `for` 串行调用 `_process_bar`。
|
||||
- `bootstrap/config.py:21` 定义 `market_data_max_workers`,但没有调用方使用。
|
||||
- `infrastructure/tushare.py:109-119` 每股调用一次六年 qfq `pro_bar`;本机
|
||||
Tushare 1.4.29 的 `pro_bar` 内部为 qfq 分别调用 `daily` 和 `adj_factor`。
|
||||
- `infrastructure/tushare.py:153-170` 只在外层请求成功后等待固定间隔;并发 worker
|
||||
之间没有共享冷却窗口。
|
||||
- Tushare 1.4.29 的 `pro_bar` 会在内部捕获原始异常、打印消息并最终抛出通用
|
||||
`IOError("ERROR.")`。如果只在 `pro_bar` 外层分类错误,将丢失“访问频繁/429”等信号。
|
||||
- `infrastructure/postgres.py` 在 `upsert_bars`、`record_item` 和每次 `_exists` 中创建连接。
|
||||
- `application/sync.py:273-278` 为覆盖率逐股票调用 `has_bar` 和 `has_daily_basic`。
|
||||
- 100 只股票、每只 1500 行的本机 CSV 探针中,读取、比较、重写和发布平均
|
||||
0.0399 秒/只,属于次要成本。
|
||||
|
||||
## 旧项目 `../zgnb-project`
|
||||
|
||||
- `application/pipeline.py:104-159` 默认 `workers=8`,使用
|
||||
`ThreadPoolExecutor` 和 `as_completed` 逐股并发。
|
||||
- `infrastructure/data_source/tushare_adapter.py:134-172` 每股仍请求六年 qfq
|
||||
`pro_bar`,不是按交易日一次请求全市场。
|
||||
- 旧实现识别“访问频繁、请稍后、超过频率、too many requests、429、403”;匹配后按
|
||||
60/120/180 秒退避,普通错误按 5/10/15 秒退避。
|
||||
- 因为 `pro_bar` 会把原始异常转换为 `IOError("ERROR.")`,旧实现的外层频控分类不能
|
||||
稳定看到原始供应商消息;新实现需要在实际 `client.daily` / `client.adj_factor`
|
||||
调用处协调频控,同时继续复用 `pro_bar` 的 qfq 计算。
|
||||
- 旧项目 `/data/check` 只用 000001 探测 Tushare 当日是否出数,不检查本地 PostgreSQL/CSV,
|
||||
因而只能参考交互入口,不能直接迁移为本任务的完整性检查。
|
||||
|
||||
## 当前技术选择
|
||||
|
||||
- 用户选择保留旧项目的数据口径:日常仍逐股请求六年 qfq,默认 8 worker。
|
||||
- 用户明确 Tushare 主要限制是调用次数;本任务不改成按交易日增量方案。
|
||||
- 用户选择完整性检查只报告,不自动修复,也不调用 Tushare。
|
||||
- Psycopg 3 官方文档确认同步 `ConnectionPool` 可由多个线程共享;
|
||||
`pool.connection()` 归还连接时自动提交或回滚,池应显式关闭或注册进程退出清理。
|
||||
|
||||
## 设计影响
|
||||
|
||||
- 单股 worker 必须返回不可变结果,由主线程汇总;不能并发修改共享 list/counter。
|
||||
- Tushare 真实请求需要一个进程内共享的请求协调器:全局最小请求间隔、频控冷却窗口、
|
||||
有界重试和可测试时钟。
|
||||
- PostgreSQL 写路径使用大小受限的连接池;同步 item 由主线程分批落库,覆盖率改为集合 SQL。
|
||||
- 完整性检查使用独立检查批次表,避免 `mode=check` 污染“最近同步批次”和选股资格查询。
|
||||
- PostgreSQL 和 CSV 在同一 advisory lock 下以只读方式流式比较,避免一次加载全市场六年数据。
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"id": "market-data-sync-performance-audit",
|
||||
"name": "market-data-sync-performance-audit",
|
||||
"title": "优化市场数据同步并增加完整校验入口",
|
||||
"description": "将逐股六年 qfq 同步改为 8 路受控并发并批量化 PostgreSQL,同时增加用户主动触发的只读 PostgreSQL/CSV 完整性检查页面。",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "yuxuanhui",
|
||||
"assignee": "yuxuanhui",
|
||||
"createdAt": "2026-08-11",
|
||||
"completedAt": "2026-08-11",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [
|
||||
"08-11-market-sync-concurrency-storage",
|
||||
"08-11-market-integrity-check-api",
|
||||
"08-11-market-integrity-check-web"
|
||||
],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{"file":".trellis/spec/backend/market-data-sync.md","reason":"检查只读审计没有破坏市场事实、CSV 或同步批次语义。"}
|
||||
{"file":".trellis/spec/backend/http-api-contracts.md","reason":"检查路由挂载、模型、状态码和同源路径。"}
|
||||
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"检查迁移、类型、测试和禁止模式。"}
|
||||
{"file":".trellis/tasks/08-11-market-integrity-check-api/prd.md","reason":"逐条核对报告范围、只读性、冲突、分页和流式验收。"}
|
||||
{"file":".trellis/tasks/08-11-market-data-sync-performance-audit/design.md","reason":"检查没有污染首页/selection,并与并发子任务正确集成。"}
|
||||
@@ -0,0 +1,37 @@
|
||||
# 市场数据完整性检查 API 设计
|
||||
|
||||
## 深模块接口
|
||||
|
||||
`RunMarketIntegrityCheck` 对 HTTP 暴露 prepare/execute/query;调用方不需要知道流式 cursor、CSV
|
||||
布局、比较算法或检查表写入细节。真实 PostgreSQL/CSV adapters 与内存测试 adapters 落在既有 ports seam。
|
||||
|
||||
## 数据模型
|
||||
|
||||
新增 `IntegrityCheckRun`、`IntegrityIssue` 和分页结果。迁移 `0003_market_integrity_checks` 创建:
|
||||
|
||||
- `market_integrity_check`:运行状态、窗口、target/checked/issue 计数、安全批次错误和时间;
|
||||
- `market_integrity_issue`:check id、稳定 issue key、item kind/key、issue type、安全 message;
|
||||
- running/created_at 和 issue check id 索引。
|
||||
|
||||
检查表不参与首页 overview 或 selection source 查询。
|
||||
|
||||
## 比较算法
|
||||
|
||||
在同一 advisory lock 中:
|
||||
|
||||
1. 读取最近已完成同步窗口和 active stock master;
|
||||
2. 比较 stock CSV;
|
||||
3. PostgreSQL 按 `(ts_code, trade_date)` 流式读取 bars,与逐股 CSV 归并比较;
|
||||
4. PostgreSQL 按 `(trade_date, ts_code)` 流式读取 daily_basic,与逐日 CSV 归并比较;
|
||||
5. 每完成一组更新 checked_count,问题分批写入;
|
||||
6. 无问题为 passed,有问题为 issues_found,批次异常为 failed。
|
||||
|
||||
事实读取使用稳定快照且不执行 DML。CSV adapter 仅打开正式文件,不创建临时文件、不调用 publish。
|
||||
|
||||
## HTTP 与恢复
|
||||
|
||||
挂载 `/api/v1/market-data/integrity-checks`。POST 原子 claim 后通过 BackgroundTasks 执行;已有 running
|
||||
返回 409。查询端点返回 Pydantic 模型并限制 page size。
|
||||
|
||||
prepare 时可把超过配置阈值且已不持有 advisory lock 的旧 running 标记为 failed;不能仅按页面刷新
|
||||
时间误杀仍在运行的任务。后台 execute 最外层保证异常落库。
|
||||
@@ -0,0 +1,5 @@
|
||||
{"file":".trellis/spec/backend/market-data-sync.md","reason":"检查必须理解 PostgreSQL/CSV 快照布局、窗口和 advisory lock 契约。"}
|
||||
{"file":".trellis/spec/backend/http-api-contracts.md","reason":"实现 market_data 业务路由、Pydantic 响应和 FastAPI 错误映射。"}
|
||||
{"file":".trellis/spec/backend/error-handling.md","reason":"保证批次/问题错误安全、可识别且不吞异常。"}
|
||||
{"file":".trellis/tasks/08-11-market-data-sync-performance-audit/design.md","reason":"提供独立检查表、流式比较、HTTP 状态和只报告边界。"}
|
||||
{"file":".trellis/tasks/08-11-market-integrity-check-api/design.md","reason":"定义本子任务的领域接口、比较算法、迁移和恢复行为。"}
|
||||
@@ -0,0 +1,13 @@
|
||||
# 实施计划
|
||||
|
||||
1. 增加领域状态、run/issue/分页模型和 ports,先写 passed/issues/failed 应用测试。
|
||||
2. 增加 `0003_market_integrity_checks` 迁移并同步 `infrastructure/schema.py`。
|
||||
3. 扩充只读 CSV 能力:stock、bar、daily_basic 的列举/读取,不复用写入路径产生副作用。
|
||||
4. 实现 PostgreSQL 流式 snapshot reader 和 integrity check store,复用前置任务连接池。
|
||||
5. 实现分组 merge comparison、问题批量落库、进度与终态收敛。
|
||||
6. 实现同 advisory lock 冲突和陈旧 running 恢复。
|
||||
7. 增加 market_data HTTP presentation、router 挂载、202/查询/错误映射测试。
|
||||
8. 把经确认的只读检查契约列为 `.trellis/spec/` 更新候选;未经用户批准不直接提升。
|
||||
9. 运行 Ruff、Pyright、pytest;配置数据库时运行迁移与完整性集成测试。
|
||||
|
||||
回滚点:迁移只新增检查表;应用回滚不影响事实表,downgrade 可独立删除检查表。
|
||||
@@ -0,0 +1,38 @@
|
||||
# 实现市场数据完整性检查 API
|
||||
|
||||
## Goal
|
||||
|
||||
提供用户主动触发、可轮询的 PostgreSQL/CSV 完整性检查;检查不访问 Tushare、不修改市场事实,
|
||||
只持久化检查进度与安全问题报告。
|
||||
|
||||
## Dependencies
|
||||
|
||||
- 依赖 `08-11-market-sync-concurrency-storage` 的连接池和共享 advisory lock 契约完成。
|
||||
|
||||
## Requirements
|
||||
|
||||
- 检查 current stock master、六年 qfq bars 和六年 daily_basic 的 PostgreSQL/CSV 一致性。
|
||||
- 报告缺失、多余、解析失败、重复键、内容不一致和窗口越界;不把正常停牌误报为缺历史日期。
|
||||
- 使用流式/分组比较,不能一次加载全市场约 700 万行。
|
||||
- 使用独立检查表和状态 `running/passed/issues_found/failed`,不复用同步批次。
|
||||
- 检查与同步使用同一 advisory lock;已有检查运行时禁止重复触发。
|
||||
- `POST` 返回 202,`GET latest` 和 `GET by id` 返回进度、终态及分页问题。
|
||||
- 后台异常和失去 worker 的陈旧 running 记录必须能收敛为 failed。
|
||||
- 除检查表外,执行前后市场事实表内容和正式 CSV 必须完全不变。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] 一致 fixture 得到 `passed`;各类不一致得到 `issues_found` 和稳定问题类型。
|
||||
- [ ] CSV 解析失败被报告且不会中断其余对象检查。
|
||||
- [ ] 检查只使用本地 DB/CSV adapters,测试能断言 Tushare port 未被构造或调用。
|
||||
- [ ] 大数据 fixture/迭代器测试证明比较按股票/交易日分组流式消费。
|
||||
- [ ] 同步持锁时检查安全失败,检查持锁时同步不能进入临界区。
|
||||
- [ ] HTTP 测试覆盖 202、409、404、503、latest/no_data、轮询进度和问题分页。
|
||||
- [ ] 迁移 upgrade/downgrade 和 schema metadata 一致;首页/selection 查询不选择检查记录。
|
||||
- [ ] 后端 Ruff、Pyright、pytest 全部通过。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 不检查 Tushare 当日是否出数。
|
||||
- 不自动修复、重试或重新下载。
|
||||
- 不引入外部队列;沿用当前进程内 BackgroundTasks。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "market-integrity-check-api",
|
||||
"name": "market-integrity-check-api",
|
||||
"title": "实现市场数据完整性检查 API",
|
||||
"description": "实现不访问 Tushare、不修改市场事实的 PostgreSQL/CSV 完整性检查批次和 HTTP 轮询接口。",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "yuxuanhui",
|
||||
"assignee": "yuxuanhui",
|
||||
"createdAt": "2026-08-11",
|
||||
"completedAt": "2026-08-11",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "08-11-market-data-sync-performance-audit",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{"file":".trellis/spec/frontend/hook-guidelines.md","reason":"检查请求适配、query key、取消和 polling 终止逻辑。"}
|
||||
{"file":".trellis/spec/frontend/state-management.md","reason":"检查没有复制服务器状态或引入不必要全局状态。"}
|
||||
{"file":".trellis/spec/frontend/quality-guidelines.md","reason":"检查格式、lint、typecheck、Vitest 和 build 证据。"}
|
||||
{"file":".trellis/tasks/08-11-market-integrity-check-web/prd.md","reason":"逐条核对导航、触发、轮询、恢复、报告和错误态。"}
|
||||
{"file":".trellis/tasks/08-11-market-data-sync-performance-audit/design.md","reason":"检查前端与已冻结 HTTP 契约及只报告边界一致。"}
|
||||
@@ -0,0 +1,26 @@
|
||||
# 同步任务检查页面设计
|
||||
|
||||
## Feature 接口
|
||||
|
||||
新增 `features/sync/api` 三件套:types、request adapters、React Query hooks。页面只调用 hooks;
|
||||
query keys 统一以 `marketIntegrity` 开头。
|
||||
|
||||
- latest query:进入页面获取最近检查;
|
||||
- check query:running 时按 id 周期轮询,终态停止;
|
||||
- trigger mutation:POST 成功后写入/失效相关 query cache 并切换到该 id。
|
||||
|
||||
## 页面状态
|
||||
|
||||
- `no_data`:解释检查范围并提供“开始完整性检查”。
|
||||
- `running`:进度条、checked/target、问题数、开始时间,按钮禁用。
|
||||
- `passed`:完成摘要和“未发现 PostgreSQL/CSV 不一致”。
|
||||
- `issues_found`:完成摘要、只报告提示和分页问题表。
|
||||
- `failed`:安全错误、重新发起入口;不会声称市场数据损坏。
|
||||
|
||||
409 时刷新 latest 并接管已有 running;其他错误保留 ApiError 状态和用户可见重试。页面刷新通过 latest
|
||||
恢复 active id,不需要 localStorage/Zustand。
|
||||
|
||||
## 路由与可访问性
|
||||
|
||||
`/sync` 加入 route tree、navigation 和 routePresentation;AppLayout active route 识别该路径。按钮、
|
||||
进度、状态和问题列表使用可访问标签,焦点不因 polling 被重置。
|
||||
@@ -0,0 +1,5 @@
|
||||
{"file":".trellis/spec/frontend/directory-structure.md","reason":"按 sync feature 垂直切片组织 API、hooks、页面和测试。"}
|
||||
{"file":".trellis/spec/frontend/hook-guidelines.md","reason":"实现 query key、AbortSignal、mutation 和条件 polling。"}
|
||||
{"file":".trellis/spec/frontend/state-management.md","reason":"服务器检查状态只进入 React Query,不复制到 Zustand。"}
|
||||
{"file":".trellis/spec/frontend/component-guidelines.md","reason":"实现可访问状态、进度、按钮和分页问题展示。"}
|
||||
{"file":".trellis/tasks/08-11-market-integrity-check-web/design.md","reason":"定义页面状态机、409 接管、刷新恢复和路由行为。"}
|
||||
@@ -0,0 +1,10 @@
|
||||
# 实施计划
|
||||
|
||||
1. 根据已冻结 OpenAPI/后端测试响应定义 sync types 和 API adapters,先写 request contract tests。
|
||||
2. 实现 latest/check queries、trigger mutation 和终态停止 polling。
|
||||
3. 新增 Sync page 的 no_data/running/passed/issues_found/failed 展示和问题分页。
|
||||
4. 启用 navigation、route tree、route presentation 与 AppLayout active path。
|
||||
5. 补页面刷新恢复、409 接管、503/网络错误、只报告提示和可访问性测试。
|
||||
6. 运行 `pnpm format:check`、`pnpm lint`、`pnpm typecheck`、`pnpm test`、`pnpm build`。
|
||||
|
||||
回滚点:`/sync` route 与导航启用可独立回滚,不影响首页和 selection。
|
||||
@@ -0,0 +1,35 @@
|
||||
# 实现同步任务检查页面
|
||||
|
||||
## Goal
|
||||
|
||||
启用现有“同步任务”导航,让用户主动触发只读市场数据完整性检查,并持续查看进度、终态和分页问题报告。
|
||||
|
||||
## Dependencies
|
||||
|
||||
- 依赖 `08-11-market-integrity-check-api` 冻结路径、字段、状态和错误码后开始实现。
|
||||
|
||||
## Requirements
|
||||
|
||||
- 新增 `/sync` 路由和 `features/sync` 垂直切片,启用桌面/移动导航入口。
|
||||
- 页面明确说明检查只比较 PostgreSQL/CSV,不访问 Tushare、不自动修复。
|
||||
- 无历史检查时可触发;running 时显示进度并禁用重复触发;终态停止轮询。
|
||||
- 展示 passed、issues_found、failed 状态、窗口、checked/target、问题数和时间。
|
||||
- 问题报告分页展示对象类型、对象 key、问题类型和安全说明。
|
||||
- 409、503、网络错误和刷新页面后的 running 恢复均有可见行为。
|
||||
- 服务器状态只放 React Query,不复制到 Zustand。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] 导航“同步任务”可用并正确激活 `/sync` route presentation。
|
||||
- [ ] 点击检查调用一次 POST,显示 accepted/running,并轮询返回的 check id。
|
||||
- [ ] running 时按钮禁用;终态停止 polling 并展示正确状态与进度。
|
||||
- [ ] 页面刷新后能从 latest running 恢复轮询。
|
||||
- [ ] issues_found 展示分页问题;passed 显示无问题;failed 显示安全错误和重试入口。
|
||||
- [ ] 页面存在明确的“只报告、不自动修复”提示。
|
||||
- [ ] API adapter、query/mutation 和页面测试通过;格式、ESLint、TypeScript、Vitest、build 全部通过。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 不提供自动修复按钮或直接启动 Tushare 同步。
|
||||
- 不在本任务中重做首页市场概览。
|
||||
- 不新增全局任务中心或 Zustand 服务器状态。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "market-integrity-check-web",
|
||||
"name": "market-integrity-check-web",
|
||||
"title": "实现同步任务检查页面",
|
||||
"description": "启用同步任务导航,提供触发、轮询、进度和异常报告页面。",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "yuxuanhui",
|
||||
"assignee": "yuxuanhui",
|
||||
"createdAt": "2026-08-11",
|
||||
"completedAt": "2026-08-11",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "08-11-market-data-sync-performance-audit",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{"file":".trellis/spec/backend/market-data-sync.md","reason":"检查并发实现仍满足市场同步完整契约和测试矩阵。"}
|
||||
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"检查 Ruff、Pyright、pytest、禁止模式和集成测试证据。"}
|
||||
{"file":"docs/adr/0004-tushare-six-year-snapshot-sync.md","reason":"检查单股事务/发布顺序、失败隔离和滚动清理没有漂移。"}
|
||||
{"file":".trellis/tasks/08-11-market-sync-concurrency-storage/prd.md","reason":"逐条核对并发、频控、数据库调用数量和兼容性验收条件。"}
|
||||
{"file":".trellis/tasks/08-11-market-data-sync-performance-audit/research/legacy-sync-and-current-bottlenecks.md","reason":"用生产基线和旧实现证据检查性能结论是否被夸大。"}
|
||||
@@ -0,0 +1,43 @@
|
||||
# 八路同步与存储批量化设计
|
||||
|
||||
## 模块接口
|
||||
|
||||
- `SyncMarketData.execute(command)`:外部接口保持不变,内部使用固定大小 executor。
|
||||
- `SyncItemOutcome`:worker 返回值,封装 result/fingerprint/failure;不暴露线程实现。
|
||||
- Tushare 请求协调器:适配器内部接口,执行一个真实 client 方法并隐藏间隔、重试和共享冷却。
|
||||
- `MarketDataRepository.record_items(...)`:批量持久化 outcomes。
|
||||
- `MarketDataRepository.count_valid_stocks(...)`:集合式覆盖率接口。
|
||||
|
||||
## 并发流
|
||||
|
||||
stock master 和 daily_basic 保持主线程顺序执行。bar 阶段把股票提交给最多 8 个 worker;每个 worker
|
||||
完成获取、CSV staging、数据库事务和 CSV 发布后返回。主线程 `as_completed` 聚合并每 100 项刷新
|
||||
审计和日志。批量审计写失败是批次级错误,不回滚已提交事实。
|
||||
|
||||
## Tushare 频控
|
||||
|
||||
由 coordinated client 代理 `pro_bar` 实际调用的 `daily` / `adj_factor`:
|
||||
|
||||
1. 请求前检查共享 cooldown;正常情况下允许最多 8 路并发;
|
||||
2. 调用原始 token client;
|
||||
3. 频控异常扩大共享 cooldown,唤醒/阻塞所有等待 worker;
|
||||
4. 普通临时错误只退避当前调用;
|
||||
5. 到达重试预算后抛出安全 `TushareSourceError`。
|
||||
|
||||
现有可配置请求间隔保留为单股成功后的温和节流,不对所有真实调用强加全局串行间隔。
|
||||
`pro_bar(..., retry_count=1)` 避免 SDK 在看不到共享 gate 的位置自行重试。测试注入 fake clock、wait
|
||||
和 client,不使用真实睡眠或网络。
|
||||
|
||||
## PostgreSQL
|
||||
|
||||
依赖调整为 Psycopg pool extra。CLI 在一次执行期间打开池并在退出时关闭;池最大连接数至少覆盖
|
||||
8 个 worker、一个主线程写连接和 advisory lock 连接。connection 不跨线程共享。
|
||||
|
||||
`record_items` 使用 `executemany` 或 COPY/staging 一次写一批。`count_valid_stocks` 从 active 股票
|
||||
连接/EXISTS 两张目标日事实表后 count,一次返回 valid count。
|
||||
|
||||
## 失败与回退
|
||||
|
||||
- executor 创建或批量审计失败记录 batch 错误并收敛状态。
|
||||
- worker 异常必须转换为 outcome,不能让 future 异常跳过进度。
|
||||
- worker 数可降为 1,得到与原串行流程等价的安全回退。
|
||||
@@ -0,0 +1,5 @@
|
||||
{"file":".trellis/spec/backend/market-data-sync.md","reason":"实施时保持六年 qfq、单股事务、CSV 原子发布、失败重试和覆盖率契约。"}
|
||||
{"file":".trellis/spec/backend/configuration-and-runtime.md","reason":"新增 worker、频控和连接池配置必须通过 Settings 与部署环境传递。"}
|
||||
{"file":"docs/adr/0004-tushare-six-year-snapshot-sync.md","reason":"并发化不得破坏快照比较、事务顺序、partial success 和滚动保留决策。"}
|
||||
{"file":".trellis/tasks/08-11-market-data-sync-performance-audit/research/legacy-sync-and-current-bottlenecks.md","reason":"提供生产基线、旧项目 8 worker/退避模式和当前 Tushare 异常隐藏问题。"}
|
||||
{"file":".trellis/tasks/08-11-market-sync-concurrency-storage/design.md","reason":"定义 worker outcome、请求协调器、连接池和批量仓储接口。"}
|
||||
@@ -0,0 +1,14 @@
|
||||
# 实施计划
|
||||
|
||||
1. 先补失败测试:worker 上限、完成顺序、单股隔离、共享频控和集合覆盖率。
|
||||
2. 在 domain/application 定义 `SyncItemOutcome`,把 `_process_bar` 重构为无共享可变状态的返回式流程。
|
||||
3. 在 Tushare adapter 增加 coordinated client/request coordinator,设置 SDK `retry_count=1`,补错误分类测试。
|
||||
4. 把 `market_data_max_workers` 默认改为 8 并完成正整数校验;CLI 注入同步用例。
|
||||
5. 引入 Psycopg connection pool,保持 advisory lock 与单股事务的独立连接语义。
|
||||
6. 增加 `record_items` 和 `count_valid_stocks`,删除编排中的逐股覆盖率查询。
|
||||
7. 用固定线程池执行 bar futures,主线程聚合、分批审计和记录有界进度。
|
||||
8. 更新 `.env.example`、Compose 参数传递、市场同步文档和依赖锁文件。
|
||||
9. 运行 `uv lock --check`、Ruff format/check、Pyright、pytest;有测试数据库时运行 market_data integration tests。
|
||||
10. 运行 5002 fake 股票无网络基准,记录最大并发、耗时和 repository 调用次数。
|
||||
|
||||
回滚点:连接池改造和并发编排分别保持独立变更;出现供应商问题时配置 worker=1。
|
||||
@@ -0,0 +1,40 @@
|
||||
# 实现八路同步与存储批量化
|
||||
|
||||
## Goal
|
||||
|
||||
在不改变逐股六年 qfq、单股票事务/CSV 发布和覆盖率业务语义的前提下,把行情阶段从串行改为
|
||||
默认 8 路受控并发,并消除 PostgreSQL 连接、审计写入和覆盖率查询的线性额外开销。
|
||||
|
||||
## Dependencies
|
||||
|
||||
- 无子任务依赖;本任务是完整性检查后端的前置任务。
|
||||
|
||||
## Requirements
|
||||
|
||||
- `ZHIXING_MARKET_DATA_MAX_WORKERS` 默认 8,CLI 实际传入同步用例;取值必须大于等于 1。
|
||||
- bar worker 只处理一只股票并返回不可变 outcome,主线程负责计数、进度和批量审计。
|
||||
- 所有 worker 共用 Tushare 请求协调器;真实 `daily` / `adj_factor` 调用能触发共享频控冷却,
|
||||
正常流量不能被一个全局固定间隔重新串行化。
|
||||
- 频控分类至少覆盖旧项目的中文提示、429 和 403;频控冷却默认 60/120/180 秒且可配置。
|
||||
- 普通临时错误有界重试;数据验证错误不重试;所有日志安全且可统计等待时间。
|
||||
- `pro_bar` 继续使用由 `pro_api(token)` 创建的 client,并保留 qfq 计算;内部隐藏重试不得绕开协调器。
|
||||
- PostgreSQL 使用线程安全连接池,每个 worker 借独立连接完成单股票事务。
|
||||
- `market_sync_item` 由主线程分批 upsert;覆盖率由一个集合查询计算。
|
||||
- CLI 参数、退出码、失败重试、advisory lock、滚动清理和摘要字段保持兼容。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] 并发测试证明默认/配置 worker 上限有效,5002 个 fake 股票不会创建 5002 个线程。
|
||||
- [ ] 不同完成顺序得到相同汇总;单股票失败不影响其他股票,失败股票不发布临时 CSV。
|
||||
- [ ] 一个 worker 命中频控后,其他 worker 在共享冷却截止前不启动新的真实供应商调用。
|
||||
- [ ] `pro_bar` 的 `daily` 与 `adj_factor` 都经过请求协调器,token client 回归测试继续通过。
|
||||
- [ ] PostgreSQL 集成测试覆盖多线程 upsert、事务回滚、批量 item 和集合覆盖率。
|
||||
- [ ] 连接获取次数受池上限约束,覆盖率不再逐股票调用 `has_bar` / `has_daily_basic`。
|
||||
- [ ] 同步用例、CLI 和 Tushare 单元测试通过;后端 Ruff、Pyright、pytest 全部通过。
|
||||
- [ ] 无网络基准记录 5002 股票调度耗时、最大并发和 repository 调用数;生产 30 分钟目标标记为部署后验证。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 不改成按交易日增量下载,不持久化 adj_factor。
|
||||
- 不增加 HTTP 或 Web 页面。
|
||||
- 不改变六年窗口、数据表业务字段或选股资格语义。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "market-sync-concurrency-storage",
|
||||
"name": "market-sync-concurrency-storage",
|
||||
"title": "实现八路同步与存储批量化",
|
||||
"description": "为逐股六年 qfq 同步接入 8 路受控并发、共享频控、连接池、批量审计和集合式覆盖率计算。",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "yuxuanhui",
|
||||
"assignee": "yuxuanhui",
|
||||
"createdAt": "2026-08-11",
|
||||
"completedAt": "2026-08-11",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "08-11-market-data-sync-performance-audit",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
<!-- @@@auto:current-status -->
|
||||
- **Active File**: `journal-1.md`
|
||||
- **Total Sessions**: 7
|
||||
- **Last Active**: 2026-08-10
|
||||
- **Total Sessions**: 8
|
||||
- **Last Active**: 2026-08-11
|
||||
<!-- @@@/auto:current-status -->
|
||||
|
||||
---
|
||||
@@ -19,7 +19,7 @@
|
||||
<!-- @@@auto:active-documents -->
|
||||
| File | Lines | Status |
|
||||
|------|-------|--------|
|
||||
| `journal-1.md` | ~185 | Active |
|
||||
| `journal-1.md` | ~222 | Active |
|
||||
<!-- @@@/auto:active-documents -->
|
||||
|
||||
---
|
||||
@@ -29,6 +29,7 @@
|
||||
<!-- @@@auto:session-history -->
|
||||
| # | Date | Title | Commits | Branch |
|
||||
|---|------|-------|---------|--------|
|
||||
| 8 | 2026-08-11 | 完成市场数据同步与完整性检查 | `7ce1154`, `8f5f504` | `develop` |
|
||||
| 7 | 2026-08-10 | 完成选股执行状态抽屉与紧凑布局 | `17237e0` | `develop` |
|
||||
| 6 | 2026-08-10 | 按原型完善选股结果分页接口 | `ed7bdda`, `3af97bf` | `develop` |
|
||||
| 5 | 2026-08-09 | 完成响应式前端交互原型设计 | `0511669` | `develop` |
|
||||
|
||||
@@ -183,3 +183,40 @@
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 8: 完成市场数据同步与完整性检查
|
||||
|
||||
**Date**: 2026-08-11
|
||||
**Task**: 完成市场数据同步与完整性检查
|
||||
**Branch**: `develop`
|
||||
|
||||
### Summary
|
||||
|
||||
按依赖顺序完成八路同步与存储批量化、市场完整性检查 API、同步任务 Web 页面;完成质量门禁、迁移离线验证和集成测试。真实 PostgreSQL 集成测试因缺少 ZHIXING_TEST_DATABASE_URL 未运行。
|
||||
|
||||
### Main Changes
|
||||
|
||||
- 完成 8 worker 受控并发、共享频控、连接池和批量审计
|
||||
- 完成 PostgreSQL/CSV 完整性检查 API 与 0003 迁移
|
||||
- 完成 /sync 页面、轮询、错误态和导航
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `7ce1154` | (see git log) |
|
||||
| `8f5f504` | (see git log) |
|
||||
|
||||
### Testing
|
||||
|
||||
- [OK] 后端 75 passed, 2 skipped;前端 48 passed
|
||||
- [OK] Ruff、Pyright、TypeScript、build、Compose 和离线迁移通过
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
### Next Steps
|
||||
|
||||
- 配置 ZHIXING_TEST_DATABASE_URL 后执行真实 PostgreSQL 集成验证
|
||||
|
||||
Reference in New Issue
Block a user