一次查找分子级Bug的经历,过程太酸爽了
“Debugging is like trying to find a needle in a haystack, except the needle is also made of hay.”Debug调试就像是在大片的干草堆中找针一样,只不过针也是由干草制成的。
在软件开发的世界里,偶尔会出现一些非常隐蔽的 Bug,这时候工程师们像探险家一样,需要深入代码的丛林,寻找隐藏在其中的“幽灵宝藏”。前段时间,我和我的团队也踏上了这样一段刺激、有趣的探险之旅。
最近繁忙的工作告一段落,我总算轻松下来了,想趁这个机会,跟大家分享我们的这次“旅途”。
01 引子
我是 ShowMeBug 的 CEO 李亚飞,是一个古老的 Ruby 工程师。由于2019年招聘工程师的噩梦经历,我立志打造一个真实模拟工作场景的 IDE,用来终结八股文、算法横行的技术招聘时代。
这个云上的 IDE 引擎,我称之为轻协同 IDE 引擎——因为它不是为了繁杂重度的工作场景准备的,而是适应于大部分人的习惯、能快速上手熟悉、加载速度快、能协同(面试用)、低延迟感,让用户感受非常友好。
多环境启动与切换
为了达成秒级启动环境的性能要求,我们设计了一套精巧的分布式文件系统架构,其核心是一个可以瞬间复制大量小文件的写时复制(COW)技术。IO吞吐能达到几万人同时在线,性能绝对是它的一大优势。
我们对此信心满满,然而没想到,很快就翻车了。
02 探险启程
2023年1月,北方已经白雪皑皑,而深圳却仍难以感受到冬天的寒意。
我和我的团队在几次打开文件树的某个文件时,会显得有点慢——当时没有人在意,按照常规思路,“网速”背了这个锅。事后我们复盘才发现,这个看似微不足道的小问题,其实正是我们开始这次探险之旅的起点。
1月底,南方的寒意缓缓侵入。这时候我们的轻协同 IDE 引擎已经开始陆续支持了 Vue2、Vue3、React、Django、Rails 等框架环境,一开始表现都很棒,加载和启动速度都很快。但是,跑了一段时间,我们开始察觉,线上环境就出现个别环境( Rails 环境)启动要 20-30s 才能完成。
虽然其他环境仍然保持了极快的加载和启动速度,但敏锐的第六感告诉我,不行,这一定有什么猫腻,如果不立即行动,势必会对用户体验带来很不好的影响。于是,我开始安排团队排查眼前这个不起眼的问题,我们的探险之旅正式开始。
03 初露希望
湿冷的冬季,夜已深,我和我们的团队依旧坐在电脑前苦苦探索,瑟瑟发抖。
探险之旅的第一站,就是老大难的问题:定位Bug。目前只有某一个环境启动很慢,其他的环境都表现不错。大家想了很多办法都没有想明白为什么,甚至怀疑这个环境的模板是不是有问题——但把代码放在本地启动,最多就2秒。
哎,太诡异了。我们在这里卡了至少一周时间,不断追踪代码,分析日志文件,尝试各种方案,都没有弄清楚一个正常的程序启动为什么会慢。我们一度陷入了疲惫和焦虑的情绪中。
“Debug 是种信仰,只有坚信自己能找到 Bug,才有可能找到 Bug。”
软件开发界一直有一个低级Bug定律:所有诡异的问题都来自一个低级原因。在这“山重水复疑无路”之际,我们决定重新审视我们的探险路径:为什么只有 Rails 更慢,其他并不慢?会不会只是一个非常微小的原因而导致?
这时候,恰好有一个架构师朋友来访,向我们建议,可以用 perf 火焰图分析看看Rails的启动过程。
perf火焰图实例
当我们用 perf来分析时,惊讶地发现:原来 Rails 的启动要加载更多的文件!紧接着,我们又重新用了一个文件读写监控的工具:fatrace,通过它,我们看到 Rails 每次启动需要读写至少 5000 个文件,但其他框架并不需要。
这才让我们突然意识到,会不会是文件系统读写速度不及预期,导致了启动变慢。
04 Bug现身
为了搞清楚是不是文件系统读写速度的问题,我们急需一个测试IO抖动的脚本。团队初步估算一下,写好这个脚本需要一些时间。
这时候,夜已深了,研发同学已经陆续下班了。我猛然想到,不是还有ChatGPT吗?让它帮忙写一个试试。
IO抖动脚本
Cool,几乎不需要改动就能用,把代码扔在服务器开跑,一测,果然发现问题:每一次文件读写都需要 10-20 ms 才能完成。实际上,一个优秀的磁盘IO读写时延应该在亚毫级,但这里至少慢了50倍。
Bingo,如同“幽灵宝藏”一般的分子级Bug逐渐显现,问题的根因已经确认:过慢的磁盘IO读写引发了一系列操作变慢,进而导致启动时间变得非常慢。
更庆幸的是,它还让我们发现了偶尔打开文件树变慢的根本原因,这也是整个系统并发能力下降的罪魁祸首。
05 迷雾追因
看到这里,大家可能会问,这套分布式文件系统莫非一直这么慢,你们为什么在之前没有发现?
非也,早在项目开始的时候,这里的时延是比较良好的,大家没有特别注意这个 IOPS 性能指标,直到我们后面才留意到,系统运行超过一个月时,IO 读写时延很容易就进入到卡顿的状态,表现就是文件系统所在主机 CPU 忽高忽低,重启就会临时恢复。
此时,探险之旅还没结束。毕竟,这个“幽灵宝藏”周围依旧笼罩着一层迷雾。
我们继续用 fatrace(监控谁在读写哪个IO) 监控线上各个候选人答题目录的 IO 读写情况,好家伙,我们发现了一个意外的情况:几乎每一秒都有一次全量的文件 stats 操作(这是一个检测文件是否有属性变化的 IO 操作)!
也就是说,比如有 1000 个候选人正在各自的 IDE 中编码,每个候选人平均有 300 个文件,就会出现每秒 30 万的 IO 操作数!
我们赶紧去查资料,根据研究数据显示,一个普通的 SSD 盘的 IOPS 最高也就到 2-3 万 。于是,我们重新测试了自己分布式文件系统的 IOPS 能力,结果发现也是 2-3 万 。
那这肯定远远达不到我们理想中的能力级别。
这时,问题更加明确:某种未知的原因导致了大量的 IOPS 的需求,引发了 IO 读写时延变长,慢了大约几十倍。
06 接近尾声
我和我的团队继续深究下去,问题已经变得非常明确了:
原来,早在去年 12月,我们上线一个监听文件增删的变化来通知各端刷新的功能。
最开始我们采用事件监听(fswatch event),因为跨了主机,所以存在1-2s的延迟。研发同学将其改为轮询实现的方案,进而引发了每秒扫描目录的 stats 行为。
当在百人以下访问时,IOPS 没有破万,还足够应对。但一旦访问量上千,便会引发 IO 变慢,进而导致系统出现各种异常:间歇导致某些关键接口 QPS 变低,进而引发系统抖动。
随着“幽灵宝藏”显露真身,这次分子级Bug的探险之旅也已经接近尾声。团队大呼:这过程实在太酸爽了!
07 技术无止境
每一个程序员在成长路上,都需要与Bug作充足的对抗,要么你勇于探索,深入代码的丛林,快速定位,挖到越来越丰富的“宝藏”,然后尽情汲取到顶级的知识,最终成为高手;或者被它打趴下, 花费大量时间都找不到问题的根源,成为芸芸众生中的一人。
当然,程序员的世界中,不单单是Debug。
当我毕业5年之后,开始意识到技术的真正价值是解决真正的社会问题。前文中我提到:我发现技术招聘真是一个极其痛苦的事:特别花面试官的时间,却又无法有效分析出候选人的技术能力,所以我创立了ShowMeBug ,用模拟实战的编程环境,解决科学评估人才的难题。
这个轻协同 IDE 技术从零开发,支持协同文件树、完全自定义的文件编辑器、协同的控制台(Console)与终端(Shell),甚至直接支持 Ctrl+P 的文件树搜索,不仅易于使用,又强大有力。
但是这还不够。要知道,追求技术精进是我们技术人的毕生追求。技术越厉害,意味着我们的参数性能越好。对于这个轻协同IDE,我们追求三个零:零配置、零启动、零延迟。其中,零启动就是本文所追求的极限:以最快的速度启动环境和切换环境。
因此,探险之旅结束后,我们进一步改进了此文件系统,设定 raid 的多磁盘冗余,采用高性能SSD,同时重新制定了新磁盘架构参数,优化相关代码,最终大幅提升了分布式文件系统的稳定性与并发能力。
截止本文结尾,ShowMeBug启动环境的平均速度为 1.3 秒,切换环境速度进入到亚秒级,仅需要 780ms。目前在全球范围的技术能力评估赛道(TSA)中,具备1-2年的领先性。
08 后记
正当我打算结束本文时,我们内部的产品吐槽群信息闪烁,点开一看:嚯,我们又发现了新Bug。
立夏已至,我们的探险之旅又即将开始。
一次查找分子级Bug的经历,过程太酸爽了的更多相关文章
- geotrellis使用(七)记录一次惨痛的bug调试经历以及求DEM坡度实践
眼看就要端午节了,屌丝还在写代码,话说过节也不给轻松,折腾了一天终于解决了一个BUG,并完成了老板安排的求DEM坡度的任务,那么就分两段来表. 一.BUG调试 首先记录一天的BUG调试,简单copy了 ...
- 最难忘的Bug调试经历
摘要:目前,著名的社区问答网站Quora上出现一个很火的讨论:你调试过最难的Bug是什么?大家纷纷留言,把自己最痛苦的一次调试经验写下来. 相信每位程序员都有过一段不堪回首地Bug调试经历,程序员一听 ...
- android一个下拉放大库bug的解决过程及思考
android一个下拉放大库bug的解决过程及思考 起因 项目中要做一个下拉缩放图片的效果,搜索了下github上面,找到了两个方案. https://github.com/Frank-Zhu/Pul ...
- Bug,项目过程中的重要数据
作者|孙敏 为什么要做Bug分析? Bug是项目过程中的一个有价值的虫子,它不只是给开发的,而是开给整个项目组的. 通过Bug我们能获得什么? 积累测试方法,增强QA的测试能力,提升产品质量 发现项目 ...
- 每个努力奋斗过的人,被不公正的际遇砸了满头包的时候,都有那么一瞬间的代入感。出生就是hard模式的人,早已经历了太多的劳其筋骨饿其体肤,再多的人为考验只会摧毁人对美好的向往。
每个努力奋斗过的人,被不公正的际遇砸了满头包的时候,都有那么一瞬间的代入感.出生就是hard模式的人,早已经历了太多的劳其筋骨饿其体肤,再多的人为考验只会摧毁人对美好的向往.
- 交叉熵代价函数——当我们用sigmoid函数作为神经元的激活函数时,最好使用交叉熵代价函数来替代方差代价函数,以避免训练过程太慢
交叉熵代价函数 machine learning算法中用得很多的交叉熵代价函数. 1.从方差代价函数说起 代价函数经常用方差代价函数(即采用均方误差MSE),比如对于一个神经元(单输入单输出,sigm ...
- JSON金额解析BUG的解决过程
[原创申明:文章为原创,欢迎非盈利性转载,但转载必须注明来源] 这是在我们开发的一个支付系统中暴露的一个BUG,问题本身比较简单,有意思的是解决问题的过程.将过程分享出来,希望能够对大家有所帮助. 一 ...
- geotrellis使用(十二)再记录一次惨痛的伪BUG调试经历(数据导入以及读取瓦片)
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 BUG还原 查找BUG 解决方案 总结 后记 一.前 ...
- <转载>linux下内存泄露查找、BUG调试
先收藏着,抽空好好看看:http://www.ibm.com/developerworks/cn/linux/l-pow-debug/ 简介 调试程序有很多方法,例如向屏幕上打印消息,使用调试器,或者 ...
- 记录一个前端bug的解决过程
人在江湖飘,哪能不挨刀. 我挨了重重一bug.严格来讲这可能是我职业生涯以来的首个悲惨经历,因为凭我的知识储备和经验,基本上任何可重现的bug都是可解的.然而这个bug却困扰了我三个月之久,它具有以下 ...
随机推荐
- 小笨自采集在线电脑壁纸 v2.0-支持移动端自适应,支持https
这周闲着没事,翻着网站,记得去年的发表的一篇文章小笨分享一款高清电脑壁纸API,主要是将孟坤壁纸改造支持https,还加了一个搜索功能.以前的壁纸是这样的: 但是,不支持移动端浏览,于是小笨趁着这周周 ...
- SpringBoot---起步依赖starter
1.简化spring应用的初始搭建和开发过程. springboot程序优点:自动配置.起步依赖(简化依赖配置).辅助功能(内置服务器...) <?xml version="1.0&q ...
- 深入消息队列MQ,看这篇就够了!
大厂面试爱问消息队列 MQ. 因为消息队列MQ,既是大型分布式系统不可缺少的中间件,也是高并发系统的基石中间件. 如果你想要快速掌握消息队列 MQ 最内核的知识,以及消息队列MQ的主流应用场景.主流产 ...
- Lodash常用函数(二)
数组/对象方法 1.remove(元素筛选,分到两个数组) 2.sortedUniq (去重,排序) 3.takeRightWhile ( 从array数组的最后一个元素开始提取元素,直到 predi ...
- 生产计划问题(动态规划)—R实现
动态规划 动态规划(英语:Dynamic programming,简称 DP),是一种在数学.管理科学.计算机科学.经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方 ...
- CentOS 的 YUM安装时卡死解决方案
YUM是基于RPM的软件包管理器 YUM is an RPM-based package manager 补充说明 Supplementary note yum命令 是在Fedora和RedHat以及 ...
- [Linux]CentOS7搭建/配置:YUM仓库/源[本地源/Web源(Apache HTTP(D))/自建源仓库]
若想搞懂整个配置过程和原理,就按照章节(1 / 2)一步一步地来. 若想直接一步到位,不想花过多时间,尽快配好,就直接看附件章节. 什么是yum源? Yum(全称为 Yellow dog Update ...
- GPFS 文件系统部署步骤
GPFS 文件系统部署步骤 参考文档: 简书网友提供: https://www.jianshu.com/p/a0ecc0838b3b?utm_campaign=maleskine&utm_co ...
- 服务器数据监控监控-Zabbix
Zabbix下载 Zabbix Sources https://www.zabbix.com/download Zabbix安装介绍 Server端 1.安装开发软件包 yum -y groupins ...
- Java中方法的定义和使用
方法的定义和使用 注意事项: 1.方法与方法之间是 平级关系 不可以嵌套定义 2.方法的位置 可以在类{}中任意位置 3.方法定义之后 之后被调用 才能被执行 4.return 关键字的作用 返回关 ...