没有事件源的 CQRS:处理事件日志失败
CQRS without Event Sourcing: handle event log failure
由于我没有在我的 CQRS 应用程序中使用事件源,我引入了一个简单的事件日志,它使我能够更新读取存储。
这意味着我的应用程序的状态更改包含两个操作:
- 正在更新写入模型状态,例如SQL 插入
- 将事件插入事件日志
两个写操作都必须作为一个原子操作发生。不幸的是,事件日志驻留在另一个数据库中,所以我不得不考虑分布式事务。
大多数 CQRS 示例处理 saga 模式,它们似乎都使用事件溯源,这使事情变得简单得多。
我的问题是 "half-finished" 状态变化,例如
- SQL插入成功
- 事件日志插入失败
我可以想出一个补偿 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?
}
有什么概念可以帮助我吗?
我考虑了以下方案来防止不同步的读取数据库:
- 每个事件都有一个序号
- 如果读取端复制检测到未处理的事件(例如接收到 40,然后接收到 42),它会在事件日志中查询事件 41。
- 如果事件 41 不可用,系统将停止复制任何事件,直到有人仔细查看。
这需要手动维护,但可以防止读取数据库不同步。
有什么真实的生活经历吗?
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.
是的 - 你重试。
由于我没有在我的 CQRS 应用程序中使用事件源,我引入了一个简单的事件日志,它使我能够更新读取存储。
这意味着我的应用程序的状态更改包含两个操作:
- 正在更新写入模型状态,例如SQL 插入
- 将事件插入事件日志
两个写操作都必须作为一个原子操作发生。不幸的是,事件日志驻留在另一个数据库中,所以我不得不考虑分布式事务。
大多数 CQRS 示例处理 saga 模式,它们似乎都使用事件溯源,这使事情变得简单得多。
我的问题是 "half-finished" 状态变化,例如
- SQL插入成功
- 事件日志插入失败
我可以想出一个补偿 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?
}
有什么概念可以帮助我吗? 我考虑了以下方案来防止不同步的读取数据库:
- 每个事件都有一个序号
- 如果读取端复制检测到未处理的事件(例如接收到 40,然后接收到 42),它会在事件日志中查询事件 41。
- 如果事件 41 不可用,系统将停止复制任何事件,直到有人仔细查看。
这需要手动维护,但可以防止读取数据库不同步。
有什么真实的生活经历吗?
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.
是的 - 你重试。