name: postmortem-writing description: 撰写有效、无指责的事后报告,包括根因分析、时间线和行动项。用于进行事件回顾、撰写事后文档或改进事件响应流程。
事后分析撰写
撰写有效、无指责的事后报告的全面指南,以推动组织学习并防止事件复发。
何时使用此技能
- 进行事后事件回顾
- 撰写事后文档
- 促进无指责的事后会议
- 识别根因和贡献因素
- 创建可操作的跟进项
- 建立组织学习文化
核心概念
1. 无指责文化
| 指责导向 | 无指责 |
|---|---|
| “谁导致了这个问题?” | “什么条件允许了这个问题发生?” |
| “某人犯了错误” | “系统允许了这个错误” |
| 惩罚个人 | 改进系统 |
| 隐藏信息 | 分享学习 |
| 害怕发声 | 心理安全 |
2. 事后分析触发因素
- SEV1 或 SEV2 事件
- 面向客户的停机时间 > 15 分钟
- 数据丢失或安全事件
- 可能严重的未遂事件
- 新的故障模式
- 需要异常干预的事件
快速开始
事后分析时间线
第 0 天:事件发生
第 1-2 天:起草事后文档
第 3-5 天:事后会议
第 5-7 天:最终化文档,创建工单
第 2 周及以上:行动项完成
季度:回顾跨事件模式
模板
模板 1:标准事后分析
# 事后分析:[事件标题]
**日期**:2024-01-15
**作者**:@alice, @bob
**状态**:草稿 | 审核中 | 终稿
**事件严重性**:SEV2
**事件持续时间**:47 分钟
## 执行摘要
2024 年 1 月 15 日,支付处理服务经历了 47 分钟的停机,影响了大约 12,000 名客户。根因是部署 v2.3.4 中的配置变更触发的数据库连接池耗尽。通过回滚到 v2.3.3 并增加连接池限制解决了事件。
**影响**:
- 12,000 名客户无法完成购买
- 估计收入损失:45,000 美元
- 创建了 847 个支持工单
- 无数据丢失或安全影响
## 时间线(所有时间 UTC)
| 时间 | 事件 |
| ----- | ----------------------------------------------- |
| 14:23 | 部署 v2.3.4 完成到生产环境 |
| 14:31 | 首次警报:`payment_error_rate > 5%` |
| 14:33 | 值班工程师 @alice 确认警报 |
| 14:35 | 开始初步调查,错误率 23% |
| 14:41 | 宣布事件为 SEV2,@bob 加入 |
| 14:45 | 识别数据库连接耗尽 |
| 14:52 | 决定回滚部署 |
| 14:58 | 开始回滚到 v2.3.3 |
| 15:10 | 回滚完成,错误率下降 |
| 15:18 | 服务完全恢复,事件解决 |
## 根因分析
### 发生了什么
v2.3.4 部署包括对数据库查询模式的更改,无意中移除了一个频繁调用端点的连接池。每个请求都打开了新的数据库连接,而不是重用池化连接。
### 为什么发生
1. **近因**:`PaymentRepository.java` 中的代码更改将池化 `DataSource` 替换为直接 `DriverManager.getConnection()` 调用。
2. **贡献因素**:
- 代码审查未捕获连接处理更改
- 没有针对连接池行为的集成测试
- 暂存环境流量较低,掩盖了问题
- 数据库连接指标警报阈值过高(90%)
3. **5 Whys 分析**:
- 为什么服务失败?→ 数据库连接耗尽
- 为什么连接耗尽?→ 每个请求打开新连接
- 为什么每个请求打开新连接?→ 代码绕过连接池
- 为什么代码绕过连接池?→ 开发者不熟悉代码库模式
- 为什么开发者不熟悉?→ 没有关于连接管理模式的文档
### 系统图
[客户端] → [负载均衡器] → [支付服务] → [数据库] ↓ 连接池(损坏) ↓ 直接连接(原因)
## 检测
### 什么奏效
- 错误率警报在部署后 8 分钟内触发
- Grafana 仪表板清晰显示连接峰值
- 值班响应迅速(2 分钟确认)
### 什么未奏效
- 数据库连接指标警报阈值过高
- 没有部署相关警报
- Canary 部署本可以更早捕获此问题
### 检测差距
部署在 14:23 完成,但首次警报直到 14:31 才触发(8 分钟)。部署感知警报本可以更快检测到问题。
## 响应
### 什么奏效
- 值班工程师快速识别数据库为问题
- 回滚决策果断
- 事件频道中沟通清晰
### 什么可以改进
- 用了 10 分钟关联问题与最近部署
- 必须手动检查部署历史
- 回滚用了 12 分钟(可以更快)
## 影响
### 客户影响
- 12,000 名唯一客户受影响
- 平均影响持续时间:35 分钟
- 847 个支持工单(受影响用户的 23%)
- 客户满意度得分下降 12 点
### 业务影响
- 估计收入损失:45,000 美元
- 支持成本:约 2,500 美元(代理时间)
- 工程时间:约 8 人小时
### 技术影响
- 数据库主节点经历高负载
- 事件期间某些副本延迟
- 系统无永久损坏
## 经验教训
### 什么做得好
1. 警报在客户报告前检测到问题
2. 团队在压力下有效协作
3. 回滚程序运行顺利
4. 沟通清晰及时
### 什么做得不好
1. 代码审查遗漏关键更改
2. 连接池测试覆盖不足
3. 暂存环境不反映生产流量
4. 警报阈值未正确调整
### 哪里幸运
1. 事件发生在工作时间,团队全在
2. 数据库处理了负载而未完全失败
3. 无其他同时发生的事件
## 行动项
| 优先级 | 行动 | 负责人 | 截止日期 | 工单 |
|----------|--------|-------|----------|--------|
| P0 | 添加连接池行为的集成测试 | @alice | 2024-01-22 | ENG-1234 |
| P0 | 降低数据库连接警报阈值到 70% | @bob | 2024-01-17 | OPS-567 |
| P1 | 文档化连接管理模式 | @alice | 2024-01-29 | DOC-89 |
| P1 | 实现部署相关警报 | @bob | 2024-02-05 | OPS-568 |
| P2 | 评估 canary 部署策略 | @charlie | 2024-02-15 | ENG-1235 |
| P2 | 用类似生产流量负载测试暂存环境 | @dave | 2024-02-28 | QA-123 |
## 附录
### 支持数据
#### 错误率图
[链接到 Grafana 仪表板快照]
#### 数据库连接图
[链接到指标]
### 相关事件
- 2023-11-02:用户服务中类似连接问题(POSTMORTEM-42)
### 参考
- [连接池最佳实践](internal-wiki/connection-pools)
- [部署运行手册](internal-wiki/deployment-runbook)
模板 2:5 Whys 分析
# 5 Whys 分析:[事件]
## 问题陈述
支付服务因数据库连接耗尽经历 47 分钟停机。
## 分析
### Why #1: 为什么服务失败?
**答案**:数据库连接耗尽,导致所有新请求失败。
**证据**:指标显示连接数在 100/100(最大),有 500+ 待处理请求。
---
### Why #2: 为什么数据库连接耗尽?
**答案**:每个传入请求打开了新的数据库连接,而不是使用连接池。
**证据**:代码差异显示直接 `DriverManager.getConnection()` 而不是池化 `DataSource`。
---
### Why #3: 为什么代码绕过连接池?
**答案**:开发者重构存储库类,无意中更改了连接获取方法。
**证据**:PR #1234 显示更改,是在修复另一个错误时做出的。
---
### Why #4: 为什么在代码审查中未捕获?
**答案**:审查者专注于功能更改(错误修复),未注意到基础设施更改。
**证据**:审查评论仅讨论业务逻辑。
---
### Why #5: 为什么没有针对此类更改的安全网?
**答案**:我们缺乏验证连接池行为的自动化测试,并且缺乏关于我们连接模式的文档。
**证据**:测试套件没有连接处理测试;维基没有关于数据库连接的文章。
## 识别出的根因
1. **主要**:缺少基础设施行为的自动化测试
2. **次要**:架构模式文档不足
3. **第三**:代码审查清单不包括基础设施考虑
## 系统性改进
| 根因 | 改进 | 类型 |
| ------------- | --------------------------------- | ---------- |
| 缺少测试 | 添加基础设施行为测试 | 预防 |
| 缺少文档 | 文档化连接模式 | 预防 |
| 审查差距 | 更新审查清单 | 检测 |
| 无 canary | 实现 canary 部署 | 缓解 |
模板 3:快速事后分析(轻微事件)
# 快速事后分析:[简短标题]
**日期**:2024-01-15 | **持续时间**:12 分钟 | **严重性**:SEV3
## 发生了什么
API 延迟因缓存刷新后的缓存未命中风暴飙升至 5 秒。
## 时间线
- 10:00 - 为配置更新启动缓存刷新
- 10:02 - 延迟警报触发
- 10:05 - 识别为缓存未命中风暴
- 10:08 - 启用缓存预热
- 10:12 - 延迟正常化
## 根因
轻微配置更新的完整缓存刷新导致雷群效应。
## 修复
- 立即:启用缓存预热
- 长期:实现部分缓存失效(ENG-999)
## 经验
不要在生产中完全刷新缓存;使用定向失效。
促进指南
运行事后会议
## 会议结构(60 分钟)
### 1. 开场(5 分钟)
- 提醒每个人无指责文化
- “我们在这里学习,而不是指责”
- 回顾会议规范
### 2. 时间线回顾(15 分钟)
- 按时间顺序走过事件
- 询问澄清问题
- 识别时间线差距
### 3. 分析讨论(20 分钟)
- 什么失败了?
- 为什么失败?
- 什么条件允许了此情况?
- 什么本可以预防?
### 4. 行动项(15 分钟)
- 头脑风暴改进
- 按影响和努力优先级排序
- 分配负责人和截止日期
### 5. 结束(5 分钟)
- 总结关键学习
- 确认行动项负责人
- 如果需要,安排跟进
## 促进技巧
- 保持讨论在轨道上
- 将指责转向系统
- 鼓励安静参与者
- 文档化不同意见
- 时间限制切线
要避免的反模式
| 反模式 | 问题 | 更好方法 |
|---|---|---|
| 指责游戏 | 关闭学习 | 专注于系统 |
| 浅层分析 | 不防止复发 | 问“为什么” 5 次 |
| 无行动项 | 浪费时间 | 总有具体下一步 |
| 不切实际的行动 | 从未完成 | 范围到可完成的任务 |
| 无跟进 | 行动被遗忘 | 在工单系统中跟踪 |
最佳实践
做
- 立即开始 - 记忆快速消退
- 具体 - 精确时间、精确错误
- 包括图表 - 视觉证据
- 分配负责人 - 无孤儿行动项
- 广泛分享 - 组织学习
不做
- 不要指名道姓 - 永远
- 不要跳过小事件 - 它们揭示模式
- 不要变成指责文档 - 那会扼杀学习
- 不要创造忙碌工作 - 行动应有意义
- 不要跳过跟进 - 验证行动完成