关于程序员:一次查找分子级Bug的经历过程太酸爽了

6次阅读

共计 3358 个字符,预计需要花费 9 分钟才能阅读完成。

“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。

立夏已至,咱们的探险之旅又行将开始。

正文完
 0