没有事件源的 CQRS:处理事件日志失败

CQRS without Event Sourcing: handle event log failure

由于我没有在我的 CQRS 应用程序中使用事件源,我引入了一个简单的事件日志,它使我能够更新读取存储。

这意味着我的应用程序的状态更改包含两个操作:

两个写操作都必须作为一个原子操作发生。不幸的是,事件日志驻留在另一个数据库中,所以我不得不考虑分布式事务。

大多数 CQRS 示例处理 saga 模式,它们似乎都使用事件溯源,这使事情变得简单得多。

我的问题是 "half-finished" 状态变化,例如

我可以想出一个补偿 SQL 操作(伪代码):

SQLTransaction.Commit(); // if this fails, all is fine. Nothing to revert
try 
{
    EventLog.Insert(event);
}
catch(Exception ex) 
{
    // Try to undo the SQL stuff.
    CompensatingSQLTransaction().Commit(); 
    // uh-oh! The commit fails!!

    // What now? Do a Retry?
}

有什么概念可以帮助我吗? 我考虑了以下方案来防止不同步的读取数据库:

这需要手动维护,但可以防止读取数据库不同步。

有什么真实的生活经历吗?

Both write operations have to happen as one atomic operation.

此时有一个非常重要的问题要提出:为什么?如果远程事件日志与记录簿不同步,对企业的成本是多少?

如果您不需要同步,那么一种直接的方法是将事件日志的副本放入与写入模型相同的数据库中。 Udi Dahan 在 Reliable Messaging Without Distributed Transactions 中讨论了这种方法。写入事务成功后,您可以将事件从 SQL 存储复制到远程事件日志。

这为您提供了一个远程事件日志,该日志始终与过去某些 状态一致,但不保证会赶上现在。

这通常就足够了;毕竟,事件日志本身就是过去的快照,当事件日志的表示被复制到消费者时,记录簿可能会发生变化。

但如果这样做不行,您的选择是找到一个提供可接受折衷方案的分布式事务引擎,或者在远程写入失败时使用 sagas 撤消对本地存储的更改。

Yan Cui 的讨论saga pattern in aws, which in turn references Caitie McCaffrey's 2015 talk on sagas in distributed systems,提出了这一点:

Because the compensating actions can also fail so we need to be able to retry them until success, which means they have to be idempotent.

In practice, there should be a reasonable upper limit on the no. of retries before you alert for human intervention.

是的 - 你重试。