chore(task): 归档 08-10 与 08-11 任务

This commit is contained in:
yuxuanhui
2026-08-11 13:32:21 +08:00
parent 8f5f504368
commit dd04933d63
33 changed files with 788 additions and 5 deletions
@@ -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": {}
}