返回

文章详情

一个不稳定的测试暴露了Redis客户端的使用后释放问题

Hacker News2026年7月22日 00:27

即使当一个bug以确定性的方式发生时,修复它也可能很困难;复现触发它的环境往往是最困难的步骤。当一个bug是非确定性的,这个过程变得更加复杂:我们需要多次执行复现bug的代码,以观察其行为,这显著增加了我们的反馈循环时间。更难的是,当bug的原因在其表现之前就已经发生了——当数据被破坏而我们没有立即注意到时。我们通常的工具专注于捕获崩溃发生时系统的状态,这并不能告诉我们为什么会发生数据损坏,只是表明它确实发生了。这是我们在redis-client库中发现的一个内存损坏bug的故事,以及帮助我们到达那里的一些经历。第一章:弹跳!跳跃!向下!向上!我们的故事开始于大卫,他是我们可扩展性团队的工程经理。在一个闲置的星期二,大卫看到他的构建失败了,似乎是由于一些不稳定的测试。他在Slack上发起了一个线程以提醒其他人,并附上了Test Engine仪表板的截图。作为一家持续集成公司,我们对不稳定的测试非常熟悉——它们是任何大型代码库不可避免的一部分。在15分钟内,团队拉响了安东绳(Andon Cord);这些测试如此不稳定,以至于阻止了我们将代码部署到生产环境。正如大卫所总结的,“[的]优先事项是让主分支不被阻塞”,因此我们一起工作,暂时将这些测试从我们的套件中排除。随着紧迫感减少,大卫和其他被这个小插曲阻碍的开发者们可以自由地回到他们的原始任务。但大卫决定采取稍微深入的看看,通过检查这些测试的可靠性,使用Buildkite的Test Engine可靠性评分,看看他能否找出它们突然变得不稳定的相关性。似乎三个最糟糕的不稳定测试的可靠性从~100%下降到……不知所踪[在2-4天前]。我查看了合并的PR,以看看是否能发现可能导致这种变化的原因,但没有看到任何明显的迹象……唯一突出的就是Redis升级?那个Redis升级是在上周五下午由一位员工工程师,兼职博客叙述者帕特里克·罗宾逊(Patrick Robinson)进行的。帕特里克喜欢以第三人称谈论自己;他最喜欢的事情之一就是深入研究复杂的bug。这里是我们的第一个线索:Redis gem升级顺利完成,未见任何生产问题的迹象。但此时,第二个假设出现了:测试不稳定是因为它们依赖于特定的事件顺序。看看,这些不稳定的测试是我们功能测试套件的一部分;它们不仅利用了RSpec,还利用了无头Selenium浏览器和一个由Web/API服务器、数据库和Redis服务器组成的测试环境。在这些情况下,不同组件之间的竞争条件是导致不稳定测试的常见原因。遗憾的是,这将使我们的调查人员偏离轨道。因此,建议是在测试设置和断言之间加入一个sleep,以看看是否能解决问题。这不是解决方案,而是一种确定不稳定性是否是由于WebSocket消息稍微迟到的方式。第二章:渐逝的黑暗 生活,似乎将逐渐消逝 每天越来越远 在我内心迷失 一切都无关紧要,别人无关紧要 接下来的星期一,在与那些被跳过的相同文件中,更多的测试间歇性失败。帕特里克与另一位我们的员工工程师合作,不同于帕特里克的是,他能实际编写前端代码。尽管需要长达30分钟产生一个失败,他们成功构建了bug的可靠重现。在排除消息在测试套件执行后送达的竞争条件的可能性后,团队将注意力集中在ActionCable和Redis之间的交互上。通过添加额外的日志记录,他们发现subscribe命令已经送达ActionCable,但并未被Redis接收。其他开发人员也报告说WebSocket在开发中经常失败,表明不仅仅是测试环境受到影响。在第三天的调试结束时,似乎毫无头绪,帕特里克收到了一个非常不寻常的错误信息,他将其发布到Slack上,最后写道:“我想晚上是时候放弃了……”:text ruby(29632,0x2a9f1f000) malloc: Double free of object 0x2a9bb6740 ruby(29632,0x2a9f1f000) malloc: *** set a breakpoint in malloc_error_break to debug 这种异常是相当不寻常的,并暗示了这个bug有多复杂。但团队仍未收集到足够的信息来识别它是如何与失败相关的,或者甚至是否相关。就像一部大片系列《刀之出路》(Knives Out)的续集,即使有所有的信息,事情仍然没有意义。然而,尽管所有的线索摆在面前,这起案件仍然似乎不可能解决。 ~ 贝诺伊特·布朗克(Benoit Blanc) 第三章:永恒的问候 你好,我在这里等你 永恒

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡