如何测试分布式系统?Actor模型虽好但难以测试


没有并行性+量化执行+确定性行为=确定性模拟测试,不多线程去测试分布式系统,从并发系统中移除并发。
FoundationDB这样的人已经公开谈论了他们沿着这条道路的经历,他们的成功是我们自己努力的主要灵感。 在过去的几个月中,我们已经在Red Planet Labs建立了功能完善的确定性仿真功能。对我们来说,这已经是一个变革性的时代,它已经大大降低了在分布式系统中查找和修复复杂的订购错误的成本。过去花了我们数周时间解决的一些问题,现在已经在一下午解决了!我们还具有测试过去很难或不可能创建的条件的能力。我们将在很长一段时间内在这些功能的基础上进行构建。
对我们而言,最终的公式是没有并行性+量化执行+确定性行为=确定性模拟。
 
没有并行
如果让模拟系统中的并发actor实际上并行执行,则实际上会限制您控制执行顺序的能力,因为从根本上不能保证其任务的执行顺序。要完全控制仿真,必须显式删除所有并行性。实现此目的的一种方法是在同一进程中的单个线程上运行所有并发的actor。 
并行性不是分布式系统的基本特征吗?如果没有并行性,如何测试任何有趣的东西?尽管分布式系统确实是并发和并行的,但是这些系统遇到的绝大多数问题是逻辑并发错误的结果,而不是实际的并行资源争用的结果。  
当您有两个并置的并发actor并行运行时,它们都可以尝试在相同的确切瞬间访问共享资源。这称为争用。通常,通过使用某种锁来解决由争用引起的问题,这实际上将允许的并行度降低为1。但是,即使没有争用,您的并发参与者仍然可能仅根据他们的访问如何相互交织而行为不端。这些问题是由于 存在于应用程序较高级别的有缺陷的并发逻辑而引起的,并且很少是由锁定引起的或已修复的问题。 
但是,当您的并发参与者不共享相同的进程或硬件时,他们首先将无法访问共享资源-他们必须使用某种异步消息传递并等待答复。他们无法抗衡。因此,这意味着在两个独立的并发参与者之间进行交互时发现的所有错误都必须是并发类型的,这意味着它们仅取决于顺序。 
没有并行性的模拟方案可以很容易地在并发空间中探索顺序,但是不能在竞争空间中探索顺序。但是,这是一个很好的权衡,因为您将在分布式系统中遇到的大多数问题都是并发类型的。 竞争问题仍然是必须测试和解决的问题。
分布式系统实现通常会尽量减少对共享资源的直接访问,即使在同一位置,也要强调消息传递样式的使用,因为这会使整个系统更加一致并且更易于理解。使用这种方法,任何可能引起争用的实现细节都将抽象到应用程序的“框架”层,在其中可以对其进行详尽的测试以验证其正确性,而不必担心更大级别视野的应用程序级并发性。使用此模式时,无并行模拟方法不会牺牲其探索顺序的能力。
 
量化执行
我们将可分块工作的想法称为可识别的流量化执行。这听起来很激烈,但是在实践中,这只是意味着您要使用提交给他们的ExecutorServices和Runnables / Callables来组织所有事情,而不是使用线程。
 
确定性行为
我们已经消除了真正的并行性的不可预测性,并通过量化执行使Actor的工作流变得透明。确定性是使模拟成为生产力所需的最终要素。
在执行的每一步,我们的模拟都必须选择一个ExecutorService并使其完成一个工作项。有很多方法可以实现选择,但是以最少的复杂性覆盖最广泛的需求集的方案是使用种子随机数生成器来随机选择一个参与者。 
通过使选择随机化,可以避免意外创建比现实世界中自然发生的排序更为有序的排序。如果您多次运行相同的仿真,则可以期望执行顺序会有所不同,从而反复探索所有理论上可能的顺序中的一部分。但是,通过使用种子随机数生成器作为该随机选择的来源,该可变性立即变得可重复。 
 
模拟测试其他注意点
在测试分布式系统时,甚至只是模拟的基本功能都具有难以置信的影响力。但是事实证明,除了仿真之外,您还可以做各种事情,使其变得更加有用。与仿真兼容的系统已竭尽全力来暴露许多子组件之间的接缝。您可以利用这些接缝在测试中创建条件,否则这些条件将很难复制。例如:

  • 阻止选择特定Actor执行以模拟垃圾回收暂停
  • 停止与逻辑“流程”关联的所有执行程序将模拟流程死亡
  • 将延迟添加到网络IO任务的执行中可模拟网络延迟