快照和恢复策略



我一直在阅读CQRS+EventSoucing模式(我希望在不久的将来应用),我发现所有甲板和演示的共同点是为您的模型状态拍摄快照以便恢复它,但是这些模式/策略都没有共享。

我想知道你是否可以谈谈你对这件事的看法和经验,特别是在以下方面:

  • 何时快照
  • 如何为快照存储建模
  • 应用程序/缓存冷启动

TL;DR:您是如何在您的CQRS+EventSourcing应用中实现Snapshotting的?利与弊?

  • 规则1:不要。
  • 规则2:不要。

对事件源模型进行快照是一种性能优化。性能优化的第一条规则是什么?没有。

具体来说,快照减少了您在存储库中尝试从事件存储中重新加载模型历史的时间。

如果您的存储库可以将模型保存在内存中,那么您就不会经常重新加载它。因此,快照带来的好处将会很小。因此:不要。

如果您可以将模型分解为aggregates,也就是说您可以将模型的历史分解为具有不重叠历史的许多实体,那么您的一个模型长模型历史将变成许多许多短历史,每个短历史描述单个实体的变化。您需要加载的每个实体历史记录将非常短,因此快照带来的损失将很小。因此:不要。

我今天工作的系统需要高性能,但不是24x7可用性。因此,在我关闭系统进行维护并重新启动它的情况下,我必须加载并重新处理所有事件存储,因为我的新系统不知道处理事件的聚合id。我需要一个更好的起点,让我的系统更有效率地重新启动。

当存储库内存缓存处于冷状态时,您担心丢失写SLA,并且您有很长的模型历史记录,需要重新加载许多事件。绑定快照可能比尝试将模型历史重构为更小的流要合理得多。好吧…

快照存储是一个读取模型——在任何时候,您都应该能够删除模型并从事件存储中的持久历史中重新构建它。

从存储库的角度来看,快照存储是缓存;如果没有可用的快照,或者如果存储本身在SLA中没有响应,则需要从初始种子状态开始重新处理整个事件历史。

服务提供者接口看起来像

interface SnapshotClient {
    SnapshotRecord getSnapshot(Identifier id)
}

SnapshotRecord将向存储库提供使用快照所需的信息。至少要包含

  1. 一个纪念品,允许存储库为快照状态补充水分
  2. 创建快照时,快照投影仪处理的最后一个事件的描述。

然后模型将从备忘录中重新生成快照状态,从事件存储中加载历史,向后扫描(即,从最近的事件开始)寻找记录在SnapshotRecord中的事件,然后按顺序应用后续事件。

SnapshotRepository本身可以是一个键值存储(对于任何给定的id最多有一条记录),但是具有blob支持的关系数据库也可以很好地工作

select * 
from snapshots s 
where id = ? 
order by s.total_events desc 
limit 1

快照投影仪和存储库是紧密耦合的——它们需要就实体在所有可能的历史记录中应该是什么状态达成一致,它们需要就如何提取/重新生成纪念品达成一致,它们需要就使用哪个id来定位快照达成一致。

紧密耦合还意味着您不需要特别担心纪念品图式;字节数组就可以了。

然而,他们不需要同意自己以前的化身。快照投影仪2.0丢弃/忽略快照投影仪1.0留下的任何快照-快照存储是只是一个缓存毕竟。

我正在设计一个应用程序,每天可能会产生数百万个事件。如果我们需要在6个月后重建视图,我们该怎么办?

这里更令人信服的答案之一是显式地对time建模。你有一个实体可以活六个月,还是有180多个实体,每个实体只活一天?会计在这里是一个很好的参考领域:在财政年度结束时,结账,下一年的账簿与结转一起打开。

Yves Reynhout经常谈论建模时间和调度;发展模型可能是一个很好的起点。

确实有几个实例需要快照。但也有一些——一个常见的例子是分类帐中的帐户。您将有数千甚至数百万的信用/借记事件产生帐户的最终BALANCE状态—如果不经常快照,那将是疯狂的。

我设计聚合时的快照方法。. NET是默认关闭的,要启用聚合或实体必须继承AggregateWithMementoEntityWithMemento,反过来你的实体必须定义RestoreSnapshot, TakeSnapshotShouldTakeSnapshot

是否拍摄快照由实体本身决定。常见的模式是

Boolean ShouldTakeSnapshot() {
    return this.Version % 50 == 0;
}

这当然会每50个事件拍一个快照。

当读取实体流时,我们做的第一件事是检查快照,然后从快照被获取的那一刻开始读取实体流的其余部分。IE:不要要求整个流,只要求我们没有快照的部分。

至于商店,你可以使用任何东西。您是对的,尽管键值存储是最好的,因为您只需要1。检查是否存在2。加载整个东西-这是kv

的理想选择

对于系统重启-我不太明白你描述的问题是什么。域服务器没有理由是有状态的,因为它在不同的时间点做不同的事情。它应该只做一件事——处理下一个命令。在处理命令的过程中,它从事件存储中加载数据(包括快照),针对产生业务异常或域事件的实体运行命令,这些事件被记录到存储中。

最新更新