Postgres事务是分布式系统的超能力
几周前,我们写道您应该“仅使用Postgres”进行持久化工作流。这篇文章引发了很多讨论,但也产生了误解。我们并不仅仅是说您应该使用存储状态在Postgres中的工作流引擎。我们意味着您的工作流系统可以且通常应该与您的应用程序存储在同一个Postgres数据库中。乍一看,这听起来似乎不是一个好主意。难道这些问题不应该分开吗?工作流状态不应该存储在一个数据库中,而应用程序数据存储在另一个数据库中吗?也许不然。在分布式系统中,协同定位是一种超能力。当工作流元数据和应用程序数据存储在同一个Postgres数据库中时,它们可以在同一数据库事务中更新。这意味着部分故障不再可能,从而使得构建能正确处理所有边缘情况的工作流变得容易得多。在这篇文章中,我们将解释为什么这是可能的,以及事务如何简化幂等性和原子性等棘手问题。事务步骤的幂等性 在分布式系统中,一个基本挑战是幂等性,尤其是对于修改数据库状态的操作。持久化工作流通过在每个步骤完成后进行检查点记录来实现容错。如果工作流被中断,它将从最后的检查点步骤恢复,而不是从头开始。然而,工作流可能在完成步骤后但在记录检查点前被中断。当它恢复时,它没有记录该步骤已经运行过,并将再次执行。这使得持久工作流本身并未解决幂等性问题。工作流引擎通常要求步骤是幂等的,以便它们可以安全地重试,而不会产生重复的副作用。例如,考虑一个向银行账户进行加款的步骤。这不是一个幂等操作:如果一个步骤将100美元添加到账户,然后失败,再次运行,将100美元再次添加到账户中,则账户总共添加了200美元,这并不正确。最常见的解决方案是添加应用程序级别的簿记,以防止这种情况。例如,您可以添加一个额外的应用支付表,以跟踪哪些付款已被应用,通过事务性地更新它,并检查以确保您不会给同一个账户重复加款:当工作流状态与应用程序数据位于同一Postgres数据库中时,我们可以消除大部分复杂性。工作流系统可以在同一个事务中写入步骤检查点并执行数据库更新,而不是在其数据库事务提交后检查点。在此,工作流使用工作流引擎提供的数据库事务执行步骤。步骤执行其数据库更新,工作流引擎记录检查点,整个事务原子性地提交:通过将数据库更新和检查点写入作为相同事务的一部分,工作流引擎可以为事务步骤提供“恰好一次”的执行语义:如果事务提交,则数据库更新和检查点都被持久记录,保证该步骤不会再次执行。如果在提交之前发生任何故障,则整个事务将回滚,包括数据库更新和检查点。当工作流恢复时,它会安全地从头重新执行该步骤。这消除了数据库更新在没有相应检查点的情况下成功的时间窗口。因此,事务步骤不再需要应用程序级的幂等性逻辑或簿记表。数据库操作要么恰好执行一次并被检查点记录,要么根本不执行。使用事务工作流的原子性 另一个在分布式系统中的经典挑战是可靠地在多个系统中执行更新,例如,更新数据库记录并向另一个系统发送通知。这比听起来复杂,因为这些操作需要原子性:它们要么同时发生,要么都不发生,即使在执行它们时发生故障(例如进程崩溃或网络故障)。例如,每当客户提交新订单时,我们可能还想开始一个工作流,将订单发送到仓库进行履行。如果没有原子性,数据库和下游系统可能会变得不一致。订单可能被提交而仓库未被通知,或者仓库可能被通知有一个从未提交的订单。解决此问题的最常见解决方案是事务性收件箱。其思想是在数据库中维护一个新的“收件箱”表。当我们需要执行原子更新时,我们运行一个单一的数据库事务,该事务同时执行:更新数据库记录,将消息写入“收件箱”表。一个单独的后台进程随后轮询收件箱表,并将其中的消息传递到目标系统。以下是可能的示例:同时执行数据库记录更新和将消息写入“收件箱”。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡