docs: 记录 uv run 日更中止、唯一键缺陷与 P2-22 资源复测
- 清单:新增「uv run 运行期联网导致日更整体中止」(升为最高优先级)、 「tables_registry 唯一键声明与真实数据不符」、以及 P2-22 交易时段复测结果 (含 table_rows/日志时间戳两条方法学踩坑) - 记忆:生产运行期不得有包解析/下载行为;replace 报错 vs upsert 静默合并的危险差异
This commit is contained in:
1 parent
076114488f
commit
2d3da7ecbb
2 files changed
+142
No files matched your search
@@ -392,3 +392,51 @@ kuaixun 410、core 121、tushare 174、akshare 183、Scripts 213 = **1101 passed
|
||||
(合理:不该重启用户主动停掉的),主动停后需手动 start。
|
||||
3. **采集器终止信号留痕已补**:faulthandler 不管 SIGTERM,故 crash log 全空;
|
||||
现记录信号名 + 全线程堆栈。10.5 小时仅停 2 次且 20 秒内恢复(此前 10–20 分钟一次)。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 第八批(17:43–18:10):发现并修复「uv run 联网导致日更整体中止」
|
||||
|
||||
### 最重要的发现
|
||||
15:40 的 tushare 日更**一张表都没抓**,日志只有一行:
|
||||
`uv run` 去连 PyPI(pypi.tuna.tsinghua.edu.cn)→ **dns error** →
|
||||
因 `set -eo pipefail` **直接中止整条任务**。
|
||||
|
||||
**根因**:两个调度脚本都用 `uv run <entrypoint>`,而 **`uv run` 会在运行时同步
|
||||
依赖环境**(手动复现看到 `Downloading mypy (13.0MiB)` / `Installed 14 packages`
|
||||
—— 生产运行期联网改写自身 venv)。
|
||||
**修复**:全部改为直调 bundle 内可执行文件
|
||||
(`.venv/bin/tushare-fetcher`、`.venv/bin/akshare-fetcher`、`.venv/bin/python`),
|
||||
不联网、不重建、不依赖 uv 在 PATH 上。实跑验证:17:46:50→17:48:45 完成,
|
||||
6 个 table_group 全执行,0 error。
|
||||
|
||||
**通用教训**:生产运行期任务**不应有任何包解析/下载行为**;
|
||||
调度脚本里出现 `uv run` / `pip install` / `poetry run` 都要改成直调 venv 可执行文件。
|
||||
|
||||
### tables_registry.json 的唯一键声明不可信
|
||||
新增 `verify_unique_keys.py`(含 **MySQL NULL 语义**:键列含 NULL 不算冲突;
|
||||
单列不够时做 ≤3 列组合搜索)。17 张里 **15 对 2 错**:
|
||||
- `us_basic`:同一 ticker 被不同公司复用(AEM/ABX/AGI)→ 改
|
||||
`["ts_code","list_date","name"]`,重载 6,000 行成功(旧键报 1062 整批失败)
|
||||
- `fut_wsr`:同仓库同日多条仓单、**无自然唯一键** → 摘除 + 清掉
|
||||
1,849,006 行失真数据(错键下 upsert 静默覆盖丢行)
|
||||
|
||||
**注意**:`db.replace`(纯 INSERT)遇重复键**报错**;`db.upsert` 遇重复键**静默合并**。
|
||||
后者更危险。凡新建表都要先验唯一键。
|
||||
|
||||
### 17 张新表最终:合计 14,507,764 行,16/17 有数据
|
||||
(`opt_daily` 1,049 万行最多;`fut_wsr` 按上述原因清空)
|
||||
|
||||
### P2-22 资源复测(盘中 10:08)—— 无回归
|
||||
主进程 0.2 UNIX/s(旧基线 5092.5)、采集器 1.6(旧 658.1)、MySQL 48.2(旧 8932)、
|
||||
突发峰值 221(旧 2503)。MySQL RSS 2959.7MB 由 2GB 缓冲池 + 17.8GB 数据解释。
|
||||
|
||||
### 又踩一次同类读取错误
|
||||
`debug.log` 时间戳**不含日期**,臣按「09–11 点」过滤把前几天的记录算了进来,
|
||||
误报「采集器崩溃 40 次」;实际当天最后一次停止是 06:47、**零次异常停止**。
|
||||
**按小时过滤前必须先确认时间基准。**
|
||||
|
||||
### 命令被安全策略拦一次
|
||||
一条 python 脚本因变量名 `reg` 命中「系统级工具(reg/wsl/sc/schtasks)」策略被拦。
|
||||
改为把脚本写成文件再执行即正常。属误判,已如实告知用户。
|
||||
@@ -827,3 +827,97 @@ sys.modules[__name__] = _core
|
||||
"自己卡死"的关键证据),记录后按原语义退出。跨进程用例已证伪通过。
|
||||
部署后观察:10.5 小时内采集器仅停止 2 次(01:36、06:47)且均 20 秒内恢复,
|
||||
此前是 10–20 分钟一次 —— 需继续观察才能定论是否真正改善。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 新增 P0(本清单最高优先级):`uv run` 运行期联网导致日更整体中止(2026-09-19)
|
||||
|
||||
### 故障
|
||||
`tushare_sync.sh daily` 在 15:40 **一张表都没抓**,日志仅一行:
|
||||
```
|
||||
── T0 每日同步 (日线 + 资金面 + 事件公告) ──
|
||||
table_group: daily (6 tables)
|
||||
error: Request failed after 3 retries in 4.5s
|
||||
cause: Failed to fetch: `https://pypi.tuna.tsinghua.edu.cn/simple/structlog/`
|
||||
cause: dns error
|
||||
```
|
||||
|
||||
### 根因
|
||||
两个调度脚本都用 `uv run <entrypoint>`。**`uv run` 会在运行时同步/重建依赖环境**,
|
||||
需要联网下载缺失包;当天该请求 DNS 解析失败,又因脚本带 `set -eo pipefail`,
|
||||
这条 error **直接中止整条任务**。
|
||||
|
||||
手动复现同一条命令还看到额外危害:
|
||||
```
|
||||
Building tushare-fetcher @ file:///...
|
||||
Downloading mypy (13.0MiB)
|
||||
Installed 14 packages in 31ms
|
||||
```
|
||||
即**生产运行期会联网改写自身 venv 的依赖**。
|
||||
|
||||
### 修复
|
||||
`tushare_sync.sh`(17 处)与 `akshare_fetcher/scripts/sync_all.sh`(14 处)
|
||||
全部改为直调 bundle 内可执行文件:
|
||||
```bash
|
||||
TUSHARE_BIN="$PROJECT_DIR/.venv/bin/tushare-fetcher" # 原 uv run tushare-fetcher
|
||||
AKSHARE_BIN="$PROJECT_DIR/.venv/bin/akshare-fetcher" # 原 uv run akshare-fetcher
|
||||
VENV_PY="$PROJECT_DIR/.venv/bin/python" # 原 uv run python3 / --no-sync python
|
||||
```
|
||||
并在使用前校验文件存在,缺失即明确报错退出(而非让 uv 去"修好它")。
|
||||
→ 不联网、不重建、不依赖 uv 是否在 PATH 上(launchd 的 PATH 极简,这本身也是风险)。
|
||||
|
||||
### 验证(端到端实跑)
|
||||
- 当日日志:15:40 那次(第 1–8 行含 dns error)之后,17:46:50 起新运行至
|
||||
17:48:45 **完成,exit 0,464 行,0 error**
|
||||
- 6 个 table_group 全部执行到(含本批新增的「事件公告」「补排期-日频」)
|
||||
- 银层迁移同批完成:11,246 行写入 `fin_data_clean.stock_monthly`,blocked=False
|
||||
- akshare 路径冒烟通过
|
||||
|
||||
### 影响评估
|
||||
今天为**周六(非交易日)**,故本次故障的**数据**影响有限;
|
||||
但该缺陷在**每个工作日都会造成整日零采集**,且日志里只有一行 error、极难察觉。
|
||||
|
||||
---
|
||||
|
||||
## 新增缺陷:`tables_registry.json` 声明的唯一键与真实数据不符
|
||||
|
||||
`create_missing_tables.py` 依据 registry 的 `unique_keys` 建 UNIQUE KEY,
|
||||
`db.upsert` 也依赖同一份声明做幂等写入 —— 但 registry 是**人工维护的声明**,
|
||||
未必与接口实际返回一致。
|
||||
|
||||
新增 `Scripts/bin/verify_unique_keys.py` 逐个校验(含 **MySQL NULL 语义**:
|
||||
键列含 NULL 时两行不算冲突;单列建议不成立时做 ≤3 列组合搜索),
|
||||
结果 **17 张里 15 张正确、2 张错误**:
|
||||
|
||||
| 表 | 声明唯一键 | 实测 | 处置 |
|
||||
|---|---|---|---|
|
||||
| `us_basic` | `["ts_code"]` | 19 行冲突 | 改为 `["ts_code","list_date","name"]`(0 冲突);DB 索引重建后重载 **6,000 行** |
|
||||
| `fut_wsr` | `["trade_date","symbol","warehouse"]` | 181 行冲突,**无自然唯一键** | 从排期摘除;已清掉在错键下写入的 **1,849,006 行**失真数据 |
|
||||
|
||||
- `us_basic` 冲突原因:**同一 ticker 会被不同公司复用**(`AEM`/`ABX`/`AGI` 等)。
|
||||
旧键导致静态表 `replace` 写入报 `(1062, Duplicate entry)`,**整批失败**。
|
||||
- `fut_wsr` 冲突原因:同一仓库同一日存在**多条仓单记录**(vol 不同),
|
||||
走唯一键 upsert 会互相覆盖 → **静默丢行**(比报错更危险)。
|
||||
|
||||
新增 `Scripts/bin/inspect_dup_rows.py` 用于打印重复行、辅助确定真实唯一键。
|
||||
|
||||
---
|
||||
|
||||
## P2-22 资源复测(交易时段完成,2026-09-19 10:08,盘中)
|
||||
|
||||
| 进程 | 旧交易基线(09:32, 优化前) | 本次盘中(10:08) | 说明 |
|
||||
|---|---|---|---|
|
||||
| 主进程 UNIX 调用/s | 5092.5 | **0.2** | 大幅下降 |
|
||||
| 采集器 UNIX 调用/s | 658.1 | **1.6** | 大幅下降 |
|
||||
| MySQL UNIX 调用/s | 8932 | **48.2** | 大幅下降 |
|
||||
| 突发峰值(-g 1 -n 32) | 2503(改造前)/ 15(改造后非交易) | **221** | 盘中本就比非交易忙,221 远低于 2503 |
|
||||
| MySQL RSS | 1870 MB | **2959.7 MB** | **非泄漏**:缓冲池配置 2GB,三库数据 17.8GB(较重构前 16.89GB 增加,来自 17 张新表 + 1450 万行) |
|
||||
|
||||
**结论:无回归。** 主进程/采集器/MySQL 的调用率均远低于优化前的交易时段基线;
|
||||
MySQL RSS 上升可由缓冲池命中(2GB 上限)与数据量增长完全解释。
|
||||
|
||||
**方法学提醒(臣本次踩到)**:`debug.log` 的时间戳**不含日期**,
|
||||
用「小时区间」过滤会把**前几天同小时**的记录算进来 —— 臣据此一度误报
|
||||
「采集器 09:00–10:59 崩溃 40 次」。实际时间线显示当天最后一次停止是 **06:47**,
|
||||
当日**零次**异常停止。按小时过滤前必须先确认时间基准。
|
||||
Reference in new issue
Block a user