dj258255/incident-lab

GitHub: dj258255/incident-lab

一个在本地环境中系统重现公开生产事故案例并记录根因分析与量化修复结果的工程实践仓库。

Stars: 0 | Forks: 0

# incident-lab 这是一份在**本地重现、修复并测量前后变化**的记录,内容涵盖了公开的故障案例和广为人知的陷阱。 读懂和自己动手引爆是两码事。如果仅仅是阅读 GitLab 删除数据库事件、Cloudflare 因为一个正则表达式宕机 27 分钟事件、丰田 14 家工厂因磁盘不足停工事件,只会留下“要验证备份”、“要小心正则表达式”这样的教训。如果亲自重现,这些教训在什么条件下成立、可以用什么来阻挡,都会以具体的数据呈现出来。 本仓库就是用来收集这些重现记录的。 ## 原则 **第一,事件优先,原理在其中。** 这不是一个用来练习教科书概念的仓库。我们要重现实际发生的事故,并在此过程中仅根据需要解释相关的内部原理(隔离级别、B-Tree、MVCC 等)。 **第二,明确证据等级。** “著名事件”和“广为人知的陷阱”是不同的。每个 session 都会标明等级,以防夸大其词。 | 等级 | 含义 | |---|---| | `E1` | 存在企业事后复盘报告或监管机构原始文档的公开事件 | | `E2` | 并非特定事件,而是供应商官方文档、CVE 或行业统计发出警告的陷阱 | | `E1·缩小` | 事件真实存在,但由于原因是物理或托管式基础设施,因此仅缩小重现相同的机制 | 未能核实来源的条目未列入目录或已被废弃。这些判定记录保存在 [CATALOG.md 的验证日志](CATALOG.md#12-근거-검증-로그-2026-07-28-브라우저-직접-확인)中。 **第三,无法重现就不写。** 每个 session 必须只需一次 `docker compose up` 就能得到相同的结果。实际的命令和原始输出将原封不动地保留在 `reproduce.md` 中。 **第四,必须写下与预期不同的地方。** 在重现过程中,您会遇到与文档描述不同的行为。这几句话正是本仓库最有价值的部分。 ## 结构 ``` sessions/<트랙><번호>-<슬러그>/ ├── README.md 사건 요약 → 재현 → 내부 원리 → 해소 → 재계측 → 예상과 달랐던 점 ├── reproduce.md 실행한 명령과 출력 원문 ├── compose.yml 재현 환경 └── results/ 전후 측정 데이터·그래프 ``` ## 赛道 | 赛道 | 数量 | 涵盖内容 | |---|---|---| | A | 21 | DB 内部·运维·复制 | | B | 50 | 问题原因出在应用程序代码·配置 | | I | 11 | 容器·内核·网络·部署 | | R | 18 | 云托管型 DB(RDS·Aurora)运维 | | S | 12 | 搜索引擎(Elasticsearch·Lucene) | | V | 8 | 向量检索·RAG 质量 | | L | 10 | LLM 服务·Agent·成本·安全 | | M | 14 | 后端与移动客户端交互 | | F | 21 | 金融·证券领域 | | X | 4 | 物理事件的缩小重现 | 完整列表和重现设计位于 [CATALOG.md](CATALOG.md)。 ## 进展状态 | 状态 | 数量 | |---|---| | 已完成 | 23 | | 进行中 | 0 | | 待处理 | 146 | - [F01 韩美克除以零的值突破了订单校验](sessions/F01-hanmac-divide-by-zero/) — NaN 比较使上下限校验失效,通过有限性保护(finiteness guard)和熔断开关(kill switch)解决。包含最小重现和 Spring 订单接收 API 重现 - [F02 三星证券幽灵股票,账本不变量缺失](sessions/F02-samsung-ghost-shares/) — 错误录入发行总数 30.66 倍的股票在无约束下提交,通过总量校验触发器和 maker-checker 阻断。实测并发录入 2 次 session 触发触发器突破的过程 - [F03 开盘 9 点访问量激增](sessions/F03-market-open-connection-storm/) — HikariCP 连接池耗尽导致响应延迟剧增,通过负载削减使中位数从 3.38 秒降至 51ms - [F13 撮合引擎价格优先·时间优先并发](sessions/F13-matching-engine-priority/) — 在简单的并行撮合中,63~96% 的成交违反了时间优先,使用单一序列化器将违规数降为 0。吞吐量无损失(基于 2 vCPU) - [F17 禁用浮点数,使用 BigDecimal](sessions/F17-bigdecimal-money/) — 重现温哥华 1982 截断事件(60,000 次 -29.9 点)和 100 万次 double 的累积误差,通过指定 scale·RoundingMode 并使用最小货币单位 long 解决 - [R14 字符集·时区雷区](sessions/R14-charset-timezone/) — 6 种重现:utf8mb3 的 emoji 处理(8.4 不是截断而是替换为 ?),767 字节索引,CONVERT_TZ 将每日聚合变成一行 NULL,以及 latin1 连接的双重编码错觉 - [B52 JPA 列表 API 的三个陷阱](sessions/B52-jpa-list-api/) — N+1(查询 21 个→1 个),游标分页的标准语法在 MySQL 中无法享受 range 优化导致比 OFFSET 更慢,以及 saveAll 因为 IDENTITY 和 merge 各自的原因而变慢(对比 1 万次 INSERT 与 1 万次 SELECT) - [A06 Gap Lock 死锁](sessions/A06-gap-lock-deadlock/) — “如果没有则插入”这一模式造成的死锁。通过 data_locks 捕获两个 Gap Lock 共存且其上的 insert intention 互相阻塞的瞬间。原方式死锁 30 次,使用 ON DUPLICATE KEY UPDATE 后降为 0 次 - [A22 有索引却用不上](sessions/A22-index-not-used/) — 在类型转换·字符集·函数·前导通配符·位运算五种条件下,保持索引不变仅修改查询,速度最高提升 1,969 倍。通过 Handler_read 计数器实测读取的行数,而非依赖 EXPLAIN 估算 - [A18 Uber 的 PostgreSQL 写入放大](sessions/A18-uber-write-amplification/) — 对索引数 x fillfactor x 更新列 9 种条件的 WAL·HOT 实测。在满页中 10 个索引导致 WAL 放大 2.2 倍(Uber 正确的条件),而在稳态 HOT 命中率 83.5% 时为 1.4 倍(反方正确的条件) - [R01 复制延迟期间提升主库导致的提交丢失](sessions/R01-replica-promotion-loss/) — 通过 GTID 差集精确查明:在收到成功响应的 927 笔赞助中,有 555 笔不在提升后的新主库中。半同步虽然丢失为 0,但在超时降级窗口期丢失又会复活。甚至重现了 Seconds_Behind_Source 在延迟几分钟后依然显示为 0 持续 19 秒的现象 - [R16 结算批处理污染缓冲池](sessions/R16-batch-cache-pollution/) — 实测表明全表扫描导致命中率降至 7%,但在 NVMe 上 p95 并未崩溃。将 midpoint 防御的实际效果从工作集恢复时间(15.2 秒 vs 0.7 秒)中分离测量 - [R17 时序日志保留数据删除](sessions/R17-timeseries-partition/) — 对比同一批 350 万行数据的 DELETE(20.5 秒,无文件回收)与 DROP PARTITION(0.12 秒,回收 0.35GB)。实测阻挡 LOCK=NONE DDL 的 MDL 队列以及导致分区裁剪失效的条件 - [R12 Performance Insights 克隆](sessions/R12-perf-insights-clone/) — 手动实现 PI 的 1 秒采样·wait 分解循环,并通过 3 个已知瓶颈区段进行验证。实测行锁以 wait/io/table/sql/handler 呈现的著名混淆现象及 data_lock_waits 鉴别方法 - [R17 实时赞助计数器的热点行](sessions/R13-slotted-counter/) — 同时测量计数器更新 9 种变体的吞吐量·一致性。通过 Slot Counter 解决了在失败率为 0% 的情况下,赞助数据莫名消失 30.9% 的更新丢失问题 - [A09 300 行样本断定 1200 万行全部为 NULL](sessions/A09-planner-stats-flip/) — 300 行样本将 null_frac 记录为 1,导致估算 1 行而实际 2400 行,嵌套循环耗时 7734ms。将 statistics target 设为 1000 后降至 164ms(提升 47 倍),且缓冲使用量仅为原来的 1/27。即便在默认值 100 下,10 次中也会出现 1 次误判 - [B31 未清除的一个 ThreadLocal 拖垮了整个 ClassLoader](sessions/B31-threadlocal-classloader-leak/) — 重新部署 300 次后,本应被废弃的 300 个 ClassLoader 全部存活,而加上 try/finally 中的 remove() 一行代码后存活数为 0。Metaspace 从 24MB 开始,经过 3773 次循环触发 OOM;更新工作线程后,泄漏的 300 个也全被回收 - [B43 快速的 DDL 与安全的 DDL 截然不同](sessions/B43-expand-contract/) — 对 300 万行表添加具有常量默认值的 ADD COLUMN 在 3ms 内无需重写即可完成,但排队等待 12 秒的事务后,会阻塞后续 3 个 SELECT 7~9 秒的锁排队。通过 3 阶段 expand-contract,将最大锁持有时间从 11316ms 缩短至 3.436ms - [F07 IPO 交叉无法确认,不是死锁而是活锁](sessions/F07-nasdaq-ipo-livelock/) — 当取消订单密度高于轮次处理时,72 次尝试中有 24 次交叉确认失败,即使重新计算执行近百次,阻塞线程数依然为 0。通过快照冻结和接收截止使失败率降至 0/72,代价是数十个订单未能反映取消操作 - [F09 IB 负油价:各层级对“价格为正”假设的破坏方式各不相同](sessions/F09-negative-price/) — 负数 tick 被 CHECK 约束拦截导致最终价停留在 0.01,账户估值虚高 3764 万美元且未触发自动平仓。保证金需求崩溃从 8.00 美元开始,在 0.10 美元时涉及 1000 份合约,通过固定 House Margin 降至 12 份合约 - [A08 缓冲池大小调整与重整的代价](sessions/A08-buffer-pool-sizing/) — 在 128M~2G 扫描中,拐点位置因访问分布而异(热数据集 512M,均匀分布 1536M);命中率 77% 实际上意味着每次查询产生 0.889 个页面磁盘 I/O;仅改变 flush_method,在相同命中率下吞吐量差距高达 2.6 倍 - [A17 改用 UUIDv7 后表并没有变小](sessions/A17-uuid-page-split/) — 使用 5 种 PK 加载 120 万行数据。没有毫秒内计数器的 UUIDv7 充电率为 54.7%,与 UUIDv4(56.7%)相同;加入计数器后升至 91.4%,与顺序 BIGINT(91.9%)相当。代价在空间和时间上产生分歧:无计数器的 UUIDv7 加载期间磁盘读取 115 页,而 UUIDv4 为 12613 页 - [A02 0.09 秒的 DDL 阻塞查询长达 20 秒](sessions/A02-mdl-storm/) — 单独的 Long Transaction 停滞时间为 0 秒,单独的 ADD COLUMN 在 0.09 秒内完成。两者重叠时,20 秒内没有一个查询完成,最大等待 20021ms。将 lock_wait_timeout 设为 2 秒后,停滞时间降至 2 秒,而 DDL 以 1205 错误失败。实测在 metadata_locks 中,SHARED_READ:PENDING 排在了 EXCLUSIVE:PENDING 之后 - [F15 一个慢订阅者导致行情停滞](sessions/F15-websocket-slow-consumer/) — 11 种扇出实现方式各执行 3 次。无限队列在 86 秒时 OOM(128MB 堆中有 95.6MB 属于慢订阅者);而无队列的直接传输在堆完好(45.5MB)的情况下,发布量从 2909 暴跌至 142/s,正常订阅者的接收量也降至原来的 1/40。通过 Conflation+截断,在保持 2936/s 的同时也能维持慢订阅者 ## 运行环境 每个 session 的中间件都包含在 `compose.yml` 中,因此只需 Docker 即可启动。 - Docker / Docker Compose - 故障注入:Toxiproxy, Pumba, tc(netem), libfaketime - 监控:Prometheus, Grafana 不过,有些程序运行在宿主机上,因此根据具体的 session 可能需要额外安装。负载生成器(k6、sysbench)和应用程序 runtime 就是如此。例如,R13 需要在宿主机上启动 Spring Boot 应用,因此需要 Java 21、Grad 和 k6。每个 session 需要哪些依赖都写在了各自 `reproduce.md` 的第一节中。 ## 注意 安全相关的 session(Log4Shell、SSRF、反序列化、供应链)**仅在隔离的本地环境中**运行。我们不会针对外部目标进行测试或公开真实的漏洞利用代码。相关 session 仅探讨防御验证所需的最小范围。 ## 约定 Session 编写约定和文档模板位于 [CONVENTIONS.md](CONVENTIONS.md)。 ## 许可证 MIT
标签:Docker, JS文件枚举, 事后分析, 后台面板检测, 域名枚举, 安全防御评估, 故障复现, 数据库, 测试用例, 版权保护, 系统稳定性, 自定义请求头, 请求拦截, 运维