如何确保包括持久化到数据库和调度事件在内的复合命令是原子命令



早上好,在我们的项目中,我们有一个REST控制器,看起来像这样(这只是伪代码,不是确切的情况;这里我给出了一个简单的例子,展示了整个概念(:

@Transactional
public ResponesEntity<Void> compisiteCreate() {
customerService.createCustomer()
productService.createProduct()
orderService.createOrder()
}

每个服务(客户、产品和订单(的工厂方法是一个单独的用例。它在数据库中持久化一条记录,如果持久化成功,则会调度相应的事件。此外,每个方法都用@Transactional进行注释,因为每个方法都由一个专用端点调用。通过这种方式,我们可以确保如果抛出异常,则既不会将记录保存在数据库中,也不会为特定场景调度事件。

现在的要求是有一个compositeCreate端点(如上所述(,它应该是原子的,即如果createOrder失败,则createCustomercreateProduct都会回滚。问题出在事件上。分派的事件是同步的,不能回滚(fire&forget(。因此,如果第三个方法失败,但前两个方法正常,那么与第一个方法相关的事件已经被调度,但记录不会存储在数据库中。

我在这里的第一个想法是用某种"逻辑"来包装这个逻辑;事件调度器会话";。在该会话中,事件不是同步调度的,而是排队直到会话结束。然后在控制器逻辑结束时,当里面的代码毫无例外地通过时,我们关闭将调度所有排队事件的会话。伪代码可能看起来像这样:

@Transactional
public ResponesEntity<Void> compisiteCreate() {
eventDispatcher.startSession()
customerService.createCustomer()
productService.createProduct()
orderService.createOrder()
eventDispatcher.flushSession()
}

事件调度程序会话将与线程相关联(例如,使用ThreadLocal(,以确保每个请求只有一个会话。如果会话未启动,则会同步调度事件,而不会发生任何更改。

预期结果:要么所有内容都被持久化并分派所有事件,要么什么都不被持久化且不分派任何事件。

问题:这个想法有道理吗?也许有人已经看到了一些缺陷?或者这个想法完全错了?有人知道如何解决这个问题吗?

如何确保包括持久化到db和调度事件在内的复合命令是原子命令?

"Outbox Pattern":将事件作为事务的一部分写入数据库;担心单独发布事件。

参见Udi Dahan 的《无分布式事务的可靠消息传递》

要么所有内容都被持久化并调度所有事件,要么什么都不被持久化且不调度任何事件。

因为我们有物理约束(例如:有限的光速(,要么全有要么全无并不能保证我们能做到。我们可以处理较弱的索赔,比如";最多一次";或";至少一次";。

"至少一次";较弱的声明实际上是有用的,但这确实意味着订阅者必须能够识别重复消息——换句话说,您需要幂等消息处理。

另请参阅de Graaw 2010:没有人需要可靠的消息

最新更新