写点什么

事故复盘:我们是怎么弄丢 1400 万条日志记录的

  • 2019-11-28
  • 本文字数:3637 字

    阅读完需:约 12 分钟

事故复盘:我们是怎么弄丢1400万条日志记录的

在这里,我们就将涉事企业姑且称为“某公司”。至于发现问题的开发人员,我们也隐去真名,称其为王二。


在本文中,我们借此机会聊聊软件开发中的人为错误,以及针对这些错误应当采取的预防措施。

从开发到生产

首先某公司部署代码库变更的基本流程总结如下:


  1. 开发人员测试变更内容(属于流程中的常识性步骤,因此不做强调)。

  2. 代码审查(仅需要一名审查员,用于审查的时间一般也不长)。

  3. 将代码 push 至预生产环境。测试预生产环境。

  4. 在 push 至生产环境前重新审查变更清单。

  5. 在 push 至生产环境过程中重新审查变更清单。

  6. 在 push 至生产环境之后重新审查变更清单。


飞行员起飞前的检查清单列表里有一句话:


测试标准实践明确指出,飞行员必须妥善记录变更清单。


毫无疑问,飞行员在起飞之前当然得对飞机进行一番全面检查,这是毋庸争论的事实。如果飞行员没有负起检查责任,或者机场工作人员坦言为了避免晚点,检查过程中省略了某些步骤,乘客怎么敢贸然登机?反正我是不敢。但不少事故记录显示,某些飞行员就是没能尽到机体检查义务,最终引发了可怕的悲剧。


某公司虽然不是航空企业,犯错的代价也不是宝贵的性命,但事故仍然严重影响到企业的业务与信誉。如果情况再严重一些,这家公司可能将彻底不复存在。


总而言之,在“某公司”里,存在着三份变更清单:


1)预生产清单;2)生产中清单;3)生产后清单。

预生产变更清单:

  1. 在团队日历当中安排 push 至生产环境的时间。

  2. 对 PR 进行审查与批准。

  3. 相关团队测试并批准各项变更。

  4. 由指定的测试人员确保所有测试用例均正常通过并符合要求。

  5. 提交申请与测试或者生产环境无冲突。

  6. 在台式机及移动设备上运行冒烟 / 回归测试。

生产中变量清单:

  1. 发布流水线中无即时事故(build 失败)。

  2. 在此期间,不存在其他部署申请。

  3. 在 Slack 中通知部署已经开始。

  4. 按下“红色按钮”开始 push 至生产环境。

生产后变更清单:

  1. 检查部署脚本日志以了解部署是否成功。

  2. 在台式机及移动设备上运行冒烟 / 回归测试。

  3. 以一小时为周期,监控所有日志及统计图表。

  4. 通知变更相关团队。

  5. 如果没有问题,在 Slack 当中宣布部署成功。


很明显,其中不少步骤都有可能发生问题,而产生问题的原因就是执行人员看不到环境的整体情况。


下面,我们从三个角度提出问题:


  1. 为什么会出问题?

  2. 本应以怎样的方式加以预防?

  3. 某公司在事后做了什么?

事故分析

先来看看问题为什么会发生,又怎么会导致大量日志丢失。


  1. PR 当中包含对某行代码的微小变更,可能影响巨大,但相关描述非常模糊。

  2. 在同一 PR 当中,三分之一的程序流并未经过王二的实际测试,因为他觉得抽查当中没发现问题就够了。

  3. 代码审查员没有注意到这一微小的变更。

  4. 王二并没有在测试中检查这些即将受到毁灭性影响的日志(注意:公司当中根本没有质量保证人员这一职务)。

  5. 身兼测试人员与代码审查员两职于一身的王二没有注意到代码未发送记录请求的问题(注意:因为没有质量保证人员,所以代码审查员必然同时兼任测试人员)。

  6. 在 push 至生产环境后的监控环节当中,由于图表中的变化与其他长期记录相比非常微小,所以日志记录丢失问题没能被及时发现。

  7. 不存在对日志及图表的每日监控机制,因此没人第一时间注意到这个问题。

  8. 下一条部署至生产环境的 PR 同样没有正确遵循检查流程,且 / 或测试人员与第 6 条一样未能发现图表中的微小变化。

本应以怎样的方式加以预防

本应执行周期为七天的日志检查

开发人员往往只会检查发布过程中 1 到 4 小时周期内的日志。这就意味着,如果 4 小时前生产系统曾经发生某些异常,那么测试人员将无法发现。理论上来说,这样的问题忽略循环可能永远存在。


事实上,王二在后来对七天内日志记录进行检查时,发现了多项错误。但到目前为止,某公司还没有任何人——从管理层到开发人员——认为有必要把监控周期设定为七天。时间本身也并不是问题,开发人员可以快速切换日志显示区间来查看过去 1 小时、1 天或者 7 天当中的记录内容。

每日监控

目前公司内缺少适当的每日监控机制。监控只在清单列出的变更周期内进行,此外不再单独投入时间。一种解决办法,就是在每天工作结束时强制要求检查日志内容。


我在拉脱维亚的 Evolution Gaming 公司工作时,团队采取的是职务轮换机制,大家轮流负责检查 Sentry 错误日志、审查待处理的 PR、向团队通报审查结果以及进行日常维护等等。在整个冲刺周期(2 周)当中,大家一直身兼常规职务与每日轮换职务两种角色。为了确保所有要求都能得到严格遵循,我们还在流程中添加了一种游戏化机制,即每一次 PR 审查都对应一定的分数奖励。


虽然大家都承认审查工作的意义,但在实际执行中,大多数人其实并不情愿把时间“浪费”在这件事上。因此,激励制度是种提升审查参与度与积极性的好办法。


某公司也可以在监控方面采用相同的方法。毕竟对大多数企业来说,用稍稍放慢一点速度的代价换取更高的发布质量无疑是划算的,而且从长远来看对每个人都有利。

双批准或者多批准模式

在我待过的团队中,但凡代码审查效果出色的,都至少要对每条 PR 进行双重批准。事实证明,这种多轮把关的方法相当可靠,因此我后来自己单飞后,也继续在个人外包合同中继续沿用这一模式。


单批准模式的主要问题在于……举个例子,大家见过只有一名飞行员的航班吗?万一其中一人忘记开启控制面板上的某个开关,至少还有另一位及时提醒提醒。

提高测试质量

无论是单元测试还是集成测试,我们都该将其视为保证生产安全的一道有力屏障。


很明显,某公司内的集成测试并没有覆盖到代码中的重要变更部分。事实上,这家公司压根就没把集成测试纳入王二团队的测试范畴,甚至多次打回了要求进行集成测试的申请。这并不是因为集成测试在技术上有多困难,只是因为上下级沟通不畅、缺乏主动性以及无知。


从主观上讲,大多数开发人员不愿进行测试的头号原因,是“我为什么要在代码已经能够正常运行的情况下,因为「不必要的麻烦事」而拖慢部署进度?”


而第二号原因,就是截止日期被压得太紧。


说起单元测试,很多朋友都觉得这就是走个过场,毕竟我们很难量化单元测试到底阻断了多少错误。


这里要强调一下,单元测试确实可以防止开发期间出现的大量错误。我们的项目需要在 push 至生产环境之前就进行过测试,这样即使 1 项测试失败,合并尝试都将中止。这就给了我们解决问题并再次 push 的时间窗口。


更重要的是,我们可以在测试运行期间严密观察。我发现很多开发人员没有养成在后台运行测试的好习惯,而由此带来的恶果,就是在问题出现后大家需要耗费大量时间回溯一切变更。


当然,即使是在观察当中,出现的大量问题也有可能让我们迷失在修改的漩涡当中。这个问题可以通过科学的重构方法进行预防。Martin Fowlers 在书中建议以积极的心态进行重构,例如在发生测试失败时,请还原最后一项变更并确保不影响测试环境。他提出的方法可以概括为“一次只改一行”。当然,我们也可以根据实际情况灵活调整,比如“一次只改两行”之类。


接下来是 TDD。有研究表明,TDD 能够帮助我们将项目中的 bug 减少 40% 到 90%。但这种从长远来看收益丰厚的方法,却往往遭到开发人员的激烈反对。不过如果各位不打算破产或者失业,还是请把 TDD 严格贯彻到位。较高的测试标准,也将为您带来更强的比较竞争优势。

实施集成测试

当然,单元测试可能很难覆盖到某些代码流,甚至相关测试更像是集成 / 单元测试的结合体。这确实是种比较棘手的情况,这里我推荐大家了解 Sandy Metz 给出的建议:



如果能在开发过程中节约成本,不妨打破规则。——Rails Conf 2013 大会,Sandy Metz


我可能会考虑在单元测试中采用集成测试方法来覆盖这次引发问题的代码行,从而尽量缩小范围。对问题进行充分记录,并在测试完成后尽快清除集成测试部分。


当然,我并不是集成测试方面的专家,但在这种情况下,集成测试确实能够防止数据日志丢失。

极限掌控力

我想聊的最后一点,来自 Jocko Willinks 的《极限掌控力》一书。我坚信,变更清单检查不足的责任,有相当一部分要由团队主管(包括技术主管或者项目经理)以及开发人员本身来承担。


开发人员与质量保证人员之所以没有严格遵循检查流程,是因为他们并不清楚这项工作的重要意义,以及一旦出现偏差有可能带来怎样的严重后果。当然,这也可能是因为主管人员不太了解流程中的某些组成部分。在这种情况下,开发人员与质量保证员必须抱有“一路上报,直到解决问题”的坚定态度。

某公司在事后做了什么

在这条 PR 被 push 至生产环境的七天之后,某公司决定开发用于比较服务器与客户端日志内容的系统。这套系统能够在服务器与客户端日志间存在差异时及时发出通报。


王二团队开始引入每周日志监控流程,但是主管仍然没有亲自跟进这项工作。


幸运的是,公司管理层已经开始进行对话,探讨如何解决未来可能出现的类似问题。


在业务流程改善方面,存在着大量可供企业选择的具体方案,而这些方案能够有效预防代码引发的内部或者外部事故。但遗憾的是,我们人类天然具有一种懒性或者说消极性,那就是只会在遭遇灾难之后才真正意识到预防准备的重要意义。

原文链接

We Crashed and Lost All Essential Data Logs. Where Did We Go Wrong?


2019-11-28 10:115555
用户头像
小智 让所有人认同的文字称不上表达

发布了 408 篇内容, 共 390.8 次阅读, 收获喜欢 1982 次。

关注

评论

发布
暂无评论
发现更多内容

程序员一定要会的软件项目管理评估方案,不做只会敲代码的码农!

Java 程序员 后端

秒懂 Java 的三种代理模式,任小龙java笔记百度云

Java 程序员 后端

程序员欣宸的文章分类汇总,javaee教程文档

Java 程序员 后端

确定要面试问我JVM吗?我打算聊一个小时的!,目前最全的《Java面试题及解析》

Java 程序员 后端

碎片化时间学这些架构知识,月薪20K还不是轻轻松松,教你解决线上频出MySQL死锁问题

Java 程序员 后端

秀儿!用SSM框架实现了支付宝的支付功能,神操作啊,大型分布式系统架构图

Java 程序员 后端

程序员必知必会之——服务网格istio概念,springboot项目案例百度云

Java 程序员 后端

精心整理全网最全Tomcat面试专题及答案(共19题,含答案解析

Java 程序员 后端

确定要面试问我JVM吗?我打算聊一个小时的!(1),linux驱动架构

Java 程序员 后端

程序员都应当知道的实用工具网站,Java400道面试题通关宝典助你进大厂

Java 程序员 后端

[架构实战营]模块二作业:微信朋友圈高性能复杂度架构

Geek_99eefd

架构实战营

算法在哈啰顺风车中的实践应用,netty实战pdf

Java 程序员 后端

精心备战30天,三天斩获阿里offer,揭秘面试流程及我的学习方向

Java 程序员 后端

秒懂数组拷贝,感知新境界,java编程思维百度云

Java 程序员 后端

神操:凭借“阿里Java脑图(1),神操作

Java 程序员 后端

神操:凭借“阿里Java脑图,mysql数据库教学视频教程

Java 程序员 后端

秋招必备!阿里产出的高并发+JVM套餐,mybatis总结

Java 程序员 后端

程序员是如何看待薪资被高估的?内容过于真实,java语言程序设计与数据结构进阶版

Java 程序员 后端

算法基础之暴力递归到动态规划,java程序员面试算法宝典pdf猿媛之家

Java 程序员 后端

碎片化时间学这些架构知识,月薪20K还不是轻轻松松(1),美团高级java面试题

Java 程序员 后端

算法入门 - 动态数组的实现(Java版本),分层架构图案例

Java 程序员 后端

算法基础之递归,java核心技术卷

Java 程序员 后端

算法宝典最新分享:Alibaba+小米,redis笔记

Java 程序员 后端

究竟是什么样的奇葩需求?威胁到程序员的头发,java高级特性编程及实战第三章

Java 程序员 后端

程序员面试时这样介绍自己的项目经验,成功率能达到98,华为od技术一面

Java 程序员 后端

立即可用的实战源码(springboot+redis+mybatis,java自学教程免费视频

Java 程序员 后端

类加载器深入剖析,2021最新华为Java校招面试题

Java 程序员 后端

精通springcloud:服务发现,Eureka API,java技术上难以解决的问题

Java 程序员 后端

程序员开发必备22个终端CLI工具也太香了(附下载地址!

Java 程序员 后端

程序员就意味着高薪?解除35岁的忧虑,一条正确的职业生涯规划

Java 程序员 后端

程序员入职国企,1周上班5小时,晒出薪资感叹,阿里P8架构师的Java大厂面试题总结

Java 程序员 后端

事故复盘:我们是怎么弄丢1400万条日志记录的_软件工程_Rolands_InfoQ精选文章