数据一致性检查

2020-07-29 02:37:00

数据库。它们是我们最关键的业务数据的唯一真理来源,但是作为工程师,我们往往会在考虑到这一点的情况下忽略工具。

我们有一个完整的监控和管理工具生态系统,用于操作我们的数据库,确保它们能够复制、扩展并且总体性能良好。类似地,许多工具伴随着数据库的查询语言,从linters和美化程序到查询构建器和对象映射器。但是,在我们的应用程序写入数据之后,几乎没有工具来验证数据是否符合预期并保持原样。

这和把一只猫放在盒子里,然后把你所有的精力都花在确保气孔保持畅通和食品输送系统正常运转上并没有完全不同,但从来不会打开盒子看看猫是死是活。

在我们开发软件时,我们在应用层中应用的业务逻辑直接表示为我们的数据在存储层中的定义方式。你刚为新地址系统添加的邮政编码表吗?什么强制要求邮政编码和地址之间存在一对多关系?是否有任何邮政编码不在0-99999的整数范围内?如果代码在范围内,但根据USPS是无效的,该怎么办?这只猫是仍然有四条腿,还是你在食物输送系统中使用的钚引起了突变?

在一个完美的世界里,我们通过编写没有bug的软件来确保这些关系,但即使这样,也存在停机,而且对于处理数据的每段代码来说,每次运行时都要检查有关数据的所有内容是不切实际的。网络闪烁可能意味着一个数据库上的更新成功,但另一个数据库上的更新不成功。软件错误可能会导致每10k行中插入1行,并将文本字段设置为空字符串。即使经过良好测试的代码具有良好的错误处理能力,也无法阻止不一致问题悄悄涌入,如果您大规模这样做,问题将变得更加复杂。

一些数据库可以使用外键、约束和其他类似概念在系统级别强制执行关系,但是这些内置检查和约束只表示可以在任何给定系统中建模的一小部分可能的特征。同样,像ActiveRecord这样的ORM可以使用验证在写入时检查数据,但是通常需要在对性能敏感的代码中禁用这些验证,并且它们本身可能包含错误。

那么,当我们针对我们的代码编写单元测试、冒烟测试、集成测试和各种测试时,为什么我们不对业务数据做同样的事情呢?

我们一直在努力思考解决这个问题的其他方法,我们已经将其编入我们所称的一致性检查框架。

构建一个响应性强、可定制的框架,能够以有意义的方式分析和报告您的数据,这会带来许多主动和被动的好处。请考虑以下使用情形:

从停机或错误中恢复-“哦,不,客户团队刚刚发布了一些代码,导致用户注册中途中断!我们已经恢复了部署,但是我们受到的影响有多严重?我们有几排孤儿报名,能修好吗?“。

数据迁移、导入/导出-“我们已经将团队计费的所有数据迁移到新的模式中。在我们开始读取之前,我们想检查一下数据是否都是准确的。你能写一些脚本来做到这一点吗?“。

主动发现错误-“我已经对整个数据库运行了一致性检查,结果显示我们有80个用户没有大的配置文件图标。新的化身代码肯定有漏洞!“。

数据的状态反映了整个系统的健康状况。随着时间的推移保持一致的数据库说明了软件的质量和稳定性。良好的单元测试覆盖率可以确保业务逻辑的完整性,同样,一致性检查应该查看数据的形状,并验证有关业务对象的重要假设。但是,需要编写多少一致性检查才能有效呢?什么样的关系值得投入时间?

您需要的检查取决于您的堆栈、数据的性质以及您在过去发现的错误类型。它们属于应用程序代码库中的其余业务逻辑。幸运的是,为这些检查建立基础相对容易。

在Slake,工程师通常会编写一些小的一次性脚本来诊断和解决他们正在分类的问题。这些脚本通常在问题修复后被删除,但偶尔也会保留下来,以防问题再次出现。

早期的一个例子是“无通道回填”,该脚本检查团队必须至少有一个公共通道才能正确操作的假设。团队元数据存储在一组数据库服务器上,频道元数据存储在另一组数据库服务器上,因此偶尔网络分区意味着我们成功创建了团队,但不是第一个频道。

这种特别的方法奏效了,但随着时间的推移,问题出现了。有时,工程师不会意识到现有的脚本,并且会从头开始重写它;在我们注意到重复之前,这个特定的脚本已经写了三次。此外,每个实现都略有不同,因此即使您知道它们的存在,也不清楚哪个版本是规范脚本。

随着模式的出现,这些脚本变成了可重用的代码,可以扫描多个团队,而不是只扫描一个团队。在很短的时间内,出现了一个框架,它支持扫描团队组并检测大量问题的能力。标准化这一行为和封装知识不仅可以防止重复,还可以为开发人员提供一种高效且经过验证的方法来诊断团队和代理底层数据。工程师们开始在开发过程中主动添加检查,这种做法很快就融入了我们的工程文化。

那么这个过程是什么样的呢?我们注意到所有的特别脚本都遵循相同的模式:扫描团队的某个子集的一些代码,检查单个团队特定问题的函数,以及报告结果的代码。因此,我们制作了扫描和报告代码的通用版本,并为检查代码创建了一个接口。

函数Team_Consistency_Domain_Length($Team){$msgs=array();if(strlen($Team[';domain';])>;21){$msgs[]=array(';type';=>;&39;domain_Too_long';,';message';=&>;&34;);}返回数组(&。True,';msgs';=>;$msgs,);}。

该函数以Team对象为参数,返回检查是否结束的数据结构(`ok`)和发现的警告消息数组(`msgs`)。

编写此函数后,它将被添加到检查地图中,以及检查的名称和检查内容的描述。然后一致性框架就接手了。

最初,您可以从命令行运行一致性检查,然后指定单个团队或扫描整个系统以查找问题。这使得检查团队中的所有数据以查找已知问题变得容易,这意味着许多客户问题过去涉及大量令人费解的问题,现在只需运行快速检查即可。然后,我们添加了一些选项,开发人员可以将这些选项指定为参数,以加快运行速度-就像只针对付费团队或自给定日期以来创建的团队运行一样。

##检查单个团队的一致性$php check_consistency.php-t T12345678##对自给定日期以来创建的所有团队运行团队首选项检查$php check_consistency.php--c team_prefs--from=1467590400

命令行框架是一个巨大的胜利,但只有工程师才能访问它,我们希望该工具可以由任何人操作。因此,我们设计了一个GUI,并将其连接到我们的内部管理系统。这使得我们的客户体验团队可以询问有关团队及其底层数据的问题-为他们提供工具来对复杂情况进行分类,并向工程师报告潜在问题。

我们现在还在试验对随机抽样的团队进行例行检查,以此作为在客户发现问题之前主动发现问题的一种主动方式。

当然,一旦我们有了自动检测问题的工具,下一个明显的步骤就是添加代码来自动修复它。我们已经这样做了,但只是为了一些一致性检查,因为许多问题需要人工干预,然后才能进行任何更改。例如,如果一个频道访客不知何故在两个频道中,我们希望向团队管理员解释这些选项,而不是只选择一个频道将其删除。在其他情况下,我们更愿意查看问题并确保了解每个特定不一致的根本原因,而不是盲目地让脚本来解决它。

修复程序框架的工作方式与检查框架大致相同。首先,我们定义了键到回调的映射,其中键是检查过程返回的错误:

如果检测到匹配检查,则调度回调,该回调负责实际修复问题并报告结果。

作为软件工程师,我们所做的大部分工作都是以前做过的。人们很容易认为,如果没有现成的问题解决方案,那么问题就不存在,或者您正在以错误的方式思考问题。

但是,我们都在不同的领域工作,有不同的限制。有时,正确的解决方案是根据您的特定业务需求构建一些自定义的内部基础设施。在本例中,它是检查特定团队数据的工具。在其他系统中,它可能是用户配置文件的数据,也可能是商店中某类产品的数据。或者,您可能需要为要导入的外部数据集编写验证代码。

无论您的业务是什么,构建工具来定期检查您的数据是使其成为您日常操作的一部分的好做法。像Slake这样的服务是运行代码、运行代码的服务器和这些服务器上存储的数据的组合。其中任何一个问题都会给我们的客户带来明显的问题。我们的一致性检查框架极大地减少了此类问题,并确保了内部和外部对我们产品的信心。