【问题标题】:Two conflicting long lived process managers两个相互冲突的长期流程管理器
【发布时间】:2021-09-15 19:16:31
【问题描述】:

假设我们有两个长期存在的流程管理器。例如,这两个传奇都经营着超过 1000 万件商品。第一个传奇为每个项目添加了一些东西。第二个传奇将它从每个项目中删除。鉴于两个流程管理器都需要几分钟来完成其工作,如果我同时运行它们,我会遇到麻烦。

这些项目中的一部分会保留价值,而其余的则不会。结果实际上接近随机,并且取决于影响特定项目的命令顺序。我想知道在失败的情况下重新调度“删除”命令是否可以解决问题。我的意思是,如果您尝试删除不存在的值,您应该等待第一个 saga 添加该值。但是,当流程管理器正在工作时,其他人可能会发送“删除”或“添加”命令。在这种情况下,我的方法会失败。

我该如何解决这样的问题? :)

【问题讨论】:

    标签: asynchronous architecture domain-driven-design cqrs distributed-system


    【解决方案1】:

    如果第一个 saga 正在运行,您似乎希望第二个 saga 不运行(并且可能直到某个取决于第一个 saga 添加的进程的某个进程才运行)。因此,显而易见的解决方案是拥有一个组件(可以是微服务,也可以是像 zookeeper/etcd/consul 这样的强一致性数据存储中的记录),它允许 sagas 开始执行。示例协议可能如下所示:

    • Saga 向组件发送一条消息,用于识别 saga 并传达开始的意图
    • 组件验证可能没有运行 saga,这会阻止此 saga 运行
    • 组件响应允许开始运行
    • 随后的 saga 尝试会导致拒绝,直到正在运行的 saga 告诉组件可以运行另一个 saga

    假设这个组件是可靠持久的,需要担心的故障模式是授予了权限,但这个组件从不处理 saga 完成的消息(原因可能包括权限消息没有被传递/处理或 saga崩溃)。再多的确认或额外的消息都无法解决这个问题(这基本上是两个将军的问题)。

    一个缓解措施是让这个组件(或监视这个组件的东西)在 saga 没有完成的情况下似乎已经过去了太多时间。无论/谁负责确保 liveness 都会调查以查看 saga 是否仍在运行,如果没有运行,则通知组件运行另一个 saga 是可以的。请注意,这并非万无一失:有问题的决策者很可能会做出错误的决定。

    【讨论】:

    • 感谢您的出色回答。我考虑了这样的解决方案,但老实说我并不喜欢它,因为它的阻塞方式。提出解决方案允许任何人执行自己的操作而无需等待,这将是完美的。也许在数学上甚至不可能实现这样的事情,或者同时执行两个 sagas 的混合结果是完全有效的。我不确定从领域的角度来看,这种行为是否有意义。面对在异步世界中阻塞应用程序的必要性,我本能地感到痛苦:)
    • 两个 sagas 对由同一服务管理的同一对象进行操作的约束使这变得困难。有可能让第一个 saga 添加的任何需要的数据脱离其自己的对象视图,而数据始终存在(这将是更多的 CQRS 方法)。
    • @ayeo 这种方法的扩展是让进程共享相同的一致状态,因此当用户启动批量更新时,它会将操作添加到这个共享文档(实际上是一个自定义命令队列优先级逻辑)并开始执行命令,当第二个用户启动她的批量更新时,它会将命令添加到同一个队列(文档)中,这将根据预期结果决定要执行的命令的顺序。这仍然会阻塞,但只会阻塞单个命令,而不是整个进程。
    【解决方案2】:

    我觉得我需要更多背景信息。虽然您没有明确说明,但第二个 saga 尝试删除第一个未添加的值是否存在问题?

    如果这是真的,一个简单的解决方案就是只使用第三种状态。

    我的意思是更明确地定义和声明项目状态。您目前似乎有两个状态 with valuewithout value,但没有任何迹象表明项目是否已准备好由第二个 saga 处理,因为第一个 saga 已经在相关项目上完成了工作。

    所以所有需要发生的事情就是第二个传奇一直在寻找以下项目:

    (with_value == true & ready_for_saga2 == true)
    

    Ready_for_saga2 或“Saga 1 处理完成”,在您的上下文中看起来更合适。

    【讨论】:

    • 删除/添加数据只是一个例子。一般来说,我的意思是两个 sagas 影响同一个项目并且该项目的最终状态取决于命令顺序的情况。在更复杂的系统中,甚至很难发现问题 - 对聚合门的相互影响可能是微妙的。我只是想最终管理一组项目的一致状态。
    • 如果您需要按特定顺序进行处理,那么您不能有 2 个独立的进程,因为它会创建“竞争条件”(有很多关于此的文章,应该很容易搜索)。跨度>
    【解决方案3】:

    我想说解决方案会根据我们要解决的实际问题而有所不同。

    假设它是一个库存,add 是添加到库存的项目,remove 是请求交付的项目。那么命令的顺序就没有那么重要了,因为您可以在将新项目添加到库存时处理交付请求。

    这将导致具有两个集合的聚合根:Items 和 PendingOrders。

    一个流程经理将新库存添加到商品 - 如果有任何订单待处理,它将在同一事务中完成这些订单并从集合中删除商品和订单。

    如果其他流程管理器添加订单(尝试移除商品),如果有剩余商品,它会立即执行此操作 - 或者在新商品到达时将订单添加到待处理的待处理订单中(并且可能会在我们处理延迟时通知某人)。

    这样,无论命令的顺序如何,我们最终都会得到相同的状态,但实际的现实问题对选择的模型有很大的影响。

    如果我们还有其他现实世界的问题,我们也可以制作一个模型。

    假设您有两个用户,每个用户都启动一个批量更新库存项目标题的过程。在这种情况下,您 - 以及用户 - 必须决定如何最好地解决这个冲突 - 什么会导致最好的现实世界结果。

    如果您希望所有项目的一致性 - 所有或没有项目应该通过一次批量更新来更新 - 我会将这些知识嵌入到新模型中。我们称之为UpdateTitlesProcesses。我们在系统中只有这个模型的一个实例。状态在进程之间共享。该模型实际上是一个命令队列,当用户发起批量操作时,它会将所有命令添加到队列中,并开始一次处理每一项。

    当第二个用户启动另一个标题更新时,我们模型中的业务逻辑将拒绝这个,因为已经有另一个更新开始了。或者如果专家说最后一次写入应该获胜,那么我们从第一个进程中删除剩余的命令并添加新的命令(同样我们应该决定如果用户发布单个标题更新应该发生什么,而不是批量更新 - 如果它被拒绝、优先处理或搁置?)。

    所以简而言之,我会说:

    1. 明确我们正在解决哪个现实世界的问题 - 以及哪种冲突解决结果最好(可能是一种权衡,通常还需要用户交互或通知)。
    2. 对此进行明确建模(其中流程、操作和冲突处理也是模型的一部分)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-07
      • 2022-09-24
      • 1970-01-01
      • 2020-06-05
      • 2012-12-29
      • 1970-01-01
      • 2017-06-23
      • 1970-01-01
      相关资源
      最近更新 更多