墨菲定律:两个功能交互,引发的一个Bug!

乔梁 | 2022-03-09

我经常被问到,“在你的测试生涯中,你遇到的最难忘的bug是什么?”对我来说,下面的事情是在几年前发生的一个错误。我曾领导一个支持谷歌应用引擎的工程生产力团队。当时,App Engine 仍处于早期阶段,要测试那些快速迭代的功能,我们面临许多挑战。我们的测试框架和流程也在不断迭代发展,所以加入团队是一个激动人心的时刻。

让这个 bug 如此令人难忘的是,我花了很长时间开发了一套全面的测试场景集合,但即使有这么多的测试场景,我还是明显地出现了一个失败,这让我摇头,想知道我怎么会错过它。即使我有多年的测试经验,构建能够充分反映现场情况的场景也是非常令人谦卑的。

我会尽量为读者提供足够的背景资料,让大家看看是否你也能确定异常情况所在。作为一个提示,这个问题是由两个应用程序引擎功能的交互造成的,所以我喜欢称这个故事为“两个功能的故事”。

功能一 - 数据存储管理 (backup, restore, delete)

谷歌应用引擎(Google App Engine)是十三年前谷歌发布的第一款云产品。它允许用户在云中构建和部署高度可伸缩的 web 应用程序。为了支持这一点,它有自己的可伸缩数据库,称为数据存储。管理控制台允许用户通过 web 界面管理应用程序及其数据存储。用户编写的应用程序由应用程序引擎根据指定的 URL 调用的请求处理程序组成。处理程序可以通过远程过程调用( RPC )机制调用应用程序引擎服务,比如数据存储。图1说明了这个流程。

这两个功能中的第一个功能是在管理控制台中提供备份、恢复和删除数据存储中选定或所有应用程序实体的能力。它是以一种巧妙的方式实现的,将其直接合并到应用程序中,而不是作为一个独立的实用程序。作为应用程序的一部分,它可以在数据存储上自由操作,并承担与应用程序中其他数据存储操作相同的计费费用。调用该功能时,流量将被发送到其处理程序,应用程序将对其进行处理。图2说明了这个请求流。

当这个令人难忘的错误发生时,这个数据存储管理功能已经很成熟,并且经过良好测试且稳定的了。并且,也没有对其进行任何变更。

功能二 - 用于迁移到高复制( HR )数据存储的实用程序

第二个特性(或者更准确地说,是一组特性)是在第一个特性发布并且稳定运行至少一年之后才上线的。它帮助用户将应用程序迁移到新的高复制( HR )数据存储。HR 数据存储比原版本更可靠,但要想它,就意味着我们需要创建一个新的应用程序,并将旧数据存储中的所有数据复制过来。

为了支持这种迁移,应用程序引擎开发人员在管理控制台中添加了两个新功能。第一个功能是将所有数据从一个应用程序的数据存储复制到另一个应用程序,第二个功能是将所有流量从一个应用程序重定向到另一个应用程序。后者特别有用,因为这意味着,新应用程序在迁移后可以无缝地接收流量。这组功能是由另一个团队编写的,我们在工程生产力方面通过创建用于测试各种数据存储迁移的流程来支持它们。迁移支持功能已经过全面测试,并发布给了开发人员。图3展示了重定向功能的请求流。

哪里会出错

因此,状况就出在我们发布这些实用程序以迁移到新的数据存储时。虽然我们很有信心,因为我们已经测试了许多不同类型和大小的数据存储实体的迁移。我们还测试了即使在迁移过程中中断它。也不会丢失数据,一切顺利。我相信这个新功能一定没有问题。但是,在发布后不久,我们就开始收到问题报告。

你看到这里,现在是时候问问你自己,“可能会出什么问题呢?”作为补充提示,问题报告声称新迁移的应用程序中的所有数据都在消失。

出了什么问题

如上所述,开发人员开始报告说,数据从他们新迁移的应用程序中消失了。这虽很少见,但当数据“消失”时,这当然是最令人不安的事,我们必须调查这是如何发生的。我们的标准流程确保我们有数据的内部备份,这些备份被迅速恢复。与此同时,我们试图重现这个问题,但在我们弄清楚发生了什么之前,我们一直都没有找到原因。正如我之前提到的,一旦我们理解了它,这是非常明显的,但这只会让我们因为错过了它更加痛苦。

真正发生的经过是这样的:在迁移并自动将流量重定向到新应用程序后,许多客户认为他们仍然需要将旧应用程序的数据删除,因此,他们使用了第一个数据存储管理功能来实现这一点。正如预期的那样,该功能向该应用程序发送请求,以便从数据存储中删除那些实体。然而这些请求现在已被自动重定向到新的应用程序,结果,先前已复制到新应用程序中的所有数据都被删除了。由于只有少数开发人员试图从他们的旧应用程序中删除数据,这就解释了为什么问题很少发生。图4说明了这个请求流。

Obvious, isn’t it, once you know what is happening.

Lessons Learned

这一切都发生在几年前,而 App Engine 如今已是基于一个截然不同、更强大的框架。数据存储迁移只是过去的事情,但这次经历给我留下了深刻的印象。 我从这次经历中学到的最重要的一点是:虽然对这些功能进行测试很重要,但将这些功能也视为整体工作流的一部分也很重要。在执行这些测试的时候,我们在迁移过程中仅仅执行了数量非常有限的步骤,并且,最后还省略了一个非常合理的步骤:尝试从旧应用程序中删除数据。我们的重点是测试数据存储中内容的可变性,而不是迁移过程中的不同步骤。正是这种关注让我们的目光远离了相对明显的失败案例。

我获得的另一个经验是,如果第一个特性的开发人员参与了第二组迁移特性(尤其是自动重定向流量的特性)的设计评审,很可能会发现这个 bug 。不幸的是,他已经加入了另一个团队。如果在一开始就有人提出类似“如果……会怎么样”的问题,很可能在设计阶段这个关键步骤都可以减少 bug 了。

最后,我们能恢复得如此之快给我留下了深刻的印象。防止数据丢失是云管理最重要的方面之一,能够从错误中恢复至少与防止错误一样重要。我非常尊重我在现场可靠性工程(SRE)领域的同事。


发表时间:February 01, 2022

原文作者:George Pirocanac

原文链接A Tale of Two Features