/~learn/s/95d1fc03
> 所有工程/运维类线上事故统一记录于此。每次修复后在本表加一行,不另开单次复盘文档。 > ⚠️ AI 修改相关模块前必须先读本表「后续注意事项」列,避免重犯。 > 历史详细复盘文档见文末链接。
| 日期 | 事故 | 根因 | 修复 | 后续注意事项 | ||
| ------ | ------ | ------ | ------ | ------------- | ||
| 2026-07-17 | 数据拉取守护僵死 + py-mini-racer 崩溃,全市场 ETF 断档 | schedule 库跑 8 天僵死(PID 活但不触发任务);py-mini-racer native 损坏;数据源仅 2 条路径无第 3 兜底 | 每日 03:00 自重启;force-reinstall py-mini-racer==0.6.0;清理 sandbox 进程 | schedule 守护必须每日自重启;dlsym 错误直接重装不要重试;数据源优先级 ≥3 条;健康判断看数据新鲜度,不能只看 PID | ||
| 2026-07-28 | 多守护并存,3 个 Python 抢拉数据,核心 ETF 停 3 天 | PID 文件防重复不可靠,bash 守护变孤儿;3 实例竞争 API+CSV;日志未截断膨胀 543MB | 重写 start/stop 用 pgrep -f 模式匹配;日志打印 [:200] 截断 | 排查"数据未更新"先 pgrep 看实例数;bash set -e 下 pgrep 需 \ | \ | true;长驻 Session keep-alive 12h+ 静默失效,须 Connection: close |
| 2026-07-29 | 人工智能 ETF 锚定创业板择时信号为空 | 同一策略重复注册双 board 状态污染;异常被 try/except 静默吞;NULL 直接入 DB | 纯数字码只注册带后缀版本;碎片源阈值 50→250;核心锚点 NULL 保护 | 禁止同一策略注册两个 board;核心锚点异常必须 ERROR+告警;load_best 阈值 ≥250 行 | ||
| 2026-07-29 | 22:00 Tier1 拉取 HTTP 无 timeout 僵死 2 小时 | SunRequests 封装无默认 timeout,TCP 静默丢包永久挂起;schedule 被阻塞导致自愈无法触发 | 所有 HTTP 加显式 timeout(30s);SIGALRM 看门狗 20min 喂狗,超时 os._exit | 所有 HTTP 必须显式 timeout;run_pending 必须看门狗兜底;排查僵死看日志时间+CPU 是否增长 | ||
| 2026-08-05 | cloudflared 隧道 31 天僵死 + 守护死亡 + 运营商 QoS,网站打不开 | cloudflared 失去全部连接但 PID 存活;守护因端口冲突(Address already in use)重试失败自杀;运营商 QoS 阻断 Cloudflare TCP 443;Tailscale 黑洞路由干扰 | run_web_daemon 启动先接管已有实例;隧道健康改用本机 127.0.0.1:20241/ready;tailscale down;重启隧道 | 隧道健康看连接数不看 PID;守护启动先接管(端口被占直接写 PID 文件);健康检查用本机 readiness,公网 curl 仅告警;本机 curl 超时≠网站挂,用外部路径验证;TCP 不通先查 netstat 有无 utun 黑洞路由 | ||
| 2026-09-11 | 截面选基数据滞后 6 天未发现,网站显示红色警告 | save_weekly_snapshot 加新列(market_cap/base_weight)时 INSERT_SQL 参数错位,snapshot 表多塞两列、portfolio 表漏传两列导致所有SQL抛错;外层 try/except 吞掉异常,调度日志只显示"推送完成";无数据新鲜度主动告警,靠用户肉眼发现 | 修正两处 INSERT 列-参数对齐;手动补算9/10截面;新增 check_data_freshness.py 21:00 定时巡检,滞后>2天 Server酱微信告警 | 改表结构铁律:CREATE/MIGRATE/INSERT列列表/VALUES占位符/参数tuple 五处同步改,改完跑最小数据验证;禁止裸except吞DB错误,关键路径必须raise+日志;数据新鲜度必须主动告警,不能等用户发现;每日21:00巡检截面/股票/行情三个关键数据日期,滞后>2天即微信推送 | ||
| 2026-09-13 | 三国杀页「全部武将/全部皮肤」tab 不渲染任何 chip(用户可见回归) | PICKERS 表声明数据源键 all='allChars'/'allSkins',renderChips 读 state[cfg.all],但 loadChars 只赋值顶层全局变量 allChars/allSkins,从未挂到 state 上 → state.allChars 恒为 undefined;既有测试只覆盖 Python 侧(SQL/API 位置参数),完全没执行前端 JS,整类运行时错误无人拦截 | loadChars 中同步 state.allChars=allChars;state.allSkins=allSkins;;新增 TestSanguoshaChipRendering(Node + DOM/localStorage/fetch stub,真实执行页面内联 JS)断言全量 tab 渲染出 chip | 改表驱动映射/接口契约必须全局搜引用方并逐 key 核查运行时存在(同源坑:_build_where 位置参数);前端内联 JS 关键渲染路径必须配真实执行回归测试,禁止只测 Python 侧;改完必须浏览器实测 DOM(querySelectorAll('.chip').length>0),接口 200 ≠ 渲染正确 | ||
| 2026-09-30 | 公网 https://quant.aohan.net 全天 HTTP 530,本地 8765 正常(用户反复遇到,误以为已修好) | cloudflared 默认 protocol=auto 首选 QUIC(UDP 443);本地网络对 QUIC 长连接存在干扰/QoS,precheck 握手能过但连接建立后持续 no recent network activity / context deadline exceeded,4 条 connIndex 反复重连退避 2→4→8→16→32s,/ready 的 readyConnections 恒为 0 → Cloudflare 边缘无法回源 → 530。根因不在 Python 应用,在隧道传输协议 | ~/.cloudflared/config.yml 增加 protocol: http2(强制 TCP 443);kill 旧隧道进程让守护按新配置重拉 | 隧道故障先看 /ready 的 readyConnections,不看 PID:PID 活着但 ready=0 即为 530 元凶;排查三连:本地 8765 是否 200 → 127.0.0.1:20241/ready 是否 200 → 公网是否 200,定位到具体层;默认 QUIC 在本机网络下不可用,已固定 http2,切勿改回 auto/quic;restart.sh 在守护模式下不重启隧道,改隧道配置后须单独 kill 隧道进程由守护重拉 |
/usr/bin/curl、/bin/kill);BSD env -u 必须放变量前
| 日期 | 文档 |
| ------ | ------ |
| 2026-05-28 | 2026-05-28-抓取稳定性复盘.md |
| 2026-07-29 | 2026-07-29-数据抓取链路故障全景复盘与防复发手册.md |
| 2026-08-01 | 2026-08-01-tushare数据入库时间排查报告.md |