3.4 KiB
3.4 KiB
Profeto 数据库与数据管线审查报告
审查日期: 2026-09-16 审查范围: 6 张表 ORM、6 个迁移文件、3 个采集器、上下文构建器、API 路由、连接池配置 结论: 发现 6 个 P1 + 5 个 P2 问题(无 P0)
P1 级问题(6 个)
P1-1: context_builder.py session 管理
- 文件:
src/llm/context_builder.py - 问题: 每个切片函数独立创建 session。回测 20 场并发需 120 个连接,池仅 15 个 → 耗尽阻塞
- 修复: build_context 创建共享 session 并传给所有切片函数,切片函数新增可选
db参数
P1-2: bzzoiro.py 内存预加载
- 文件:
src/data/bzzoiro.py - 问题: 预加载联赛全部比赛到内存(
select(Match).where(Match.league_id == league.id)),多赛季采集时内存溢出 - 修复: 改为按 raw_events 日期范围 ±30 天按需加载
P1-3: understat.py N+1 查询
- 文件:
src/data/understat.py - 问题: 逐条查询比赛(每场 3 次 DB 往返),单赛季 380 次 = 1140 次往返
- 修复: 批量查询球队(1 次) + 批量查询比赛(1 次) + 内存匹配
P1-4: injuries.py 幂等检查竞态条件
- 文件:
src/data/injuries.py - 问题: check-then-insert 模式,并发采集触发 IntegrityError 导致整批回滚
- 修复: 批量幂等检查 + 分批 flush + 失败回退逐条插入
P1-5: predict.py 同步 Lock 用于 async
- 文件:
src/llm/predict.py - 问题:
threading.Lock用于 async 函数,阻塞事件循环 - 修复: 删除 Lock,dict 操作在 CPython GIL 下原子
P1-6: models.py 缺唯一约束
- 文件:
src/db/models.py - 问题: 缺
(match_id, provider, model)唯一约束,重复预测导致统计偏差 - 修复: 添加 UniqueConstraint + 迁移文件
P2 级问题(5 个)
P2-1: unit_of_work 类型注解错误
- 文件:
src/db/unit_of_work.py - 问题:
AsyncIterator[AsyncSessionLocal]应为AsyncIterator[AsyncSession]
P2-2: normalize.py 日期解析静默丢弃
- 文件:
src/data/normalize.py - 问题:
_parse_date失败返回 None,数据丢失无感知 - 修复: 解析失败时
logger.warning记录原始值
P2-3: repositories.py func.date() 索引失效
- 文件:
src/db/repositories.py - 问题:
func.date(Match.match_date)致索引失效,全表扫描 - 修复: 改用
Match.match_date_date等值匹配
P2-4: 迁移 0005 幽灵列
- 文件:
alembic/versions/0005 - 问题:
cutoff_at列被注释掉未删除,autogenerate 噪声 - 状态: 已在 0006 迁移中修复
P2-5: 迁移 0003 约束命名不一致
- 文件:
alembic/versions/0003_injuries.py - 问题: UniqueConstraint vs ORM 的 Index(unique=True) 不一致
- 修复: 新迁移 0007 将 UniqueConstraint 替换为唯一索引
值得肯定的设计
- 索引/约束设计合理(唯一约束防重、CHECK 约束、复合索引)
- 数据血缘追踪(source / retrieved_at / available_at)完善
- 采集管线有重试、限速、防回测泄漏
get_db/get_db_read读写分离- 迁移规范,有 downgrade
修复验证
- 全部文件语法检查通过
- 11 个文件修改 + 2 个新迁移
- MR #3 已提交: #3
- 合并后运行
alembic upgrade head - 20 场并发回测压测验证连接池