快照采取和恢复策略

时间:2016-06-24 20:37:38

标签: cqrs snapshot event-sourcing

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

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

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

TL; DR:您如何在Snapshotting申请中实施CQRS+EventSourcing?优点和缺点?

2 个答案:

答案 0 :(得分:11)

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

快照事件源模型是一种性能优化。性能优化的第一条规则?唐'吨

具体而言,快照可以减少您在存储库中丢失的时间,从而尝试从事件存储库重新加载模型的历史记录。

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

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

  

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

当存储库内存缓存很冷时,您担心错过写入SLA,并且您有很长的模型历史记录,需要重新加载大量事件。对快照进行限制可能比尝试将模型历史重构为较小的流更合理。行....

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

从存储库的角度来看,快照存储是一个缓存;如果没有可用的快照,或者商店本身在SLA内没有响应,您希望从初始种子状态开始重新处理整个事件历史记录。

服务提供者界面看起来像

interface SnapshotClient {
    SnapshotRecord getSnapshot(Identifier id)
}

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

  1. 一个 memento ,允许存储库重新水化快照状态
  2. 构建快照时快照投影仪处理的最后一个事件的说明。
  3. 然后,模型将从纪念品中重新保存快照状态,从事件存储加载历史记录,向后扫描(即从最近的事件开始),查找记录在SnapshotRecord,然后按顺序应用后续事件。

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

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

    快照投影仪和存储库是紧密耦合的 - 他们需要就所有可能的历史实体状态达成一致意见,他们需要就如何对纪念品进行重新融合达成一致,他们需要同意将使用哪个id来定位快照。

    紧耦合也意味着你不需要特别担心 纪念品的图式;一个字节数组就可以了。

    但是,他们不需要同意以前的自己的化身。 Snapshot Projector 2.0丢弃/忽略Snapshot Projector 1.0遗留的任何快照 - 快照存储只是一个缓存

      

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

    此处一个更引人注目的答案是明确地模拟时间。您是否有一个存在六个月的实体,或者您有180多个实体,每个实体都有一天生活?会计是一个很好的领域,在这里可以参考:在本财政年度结束时,书籍将被关闭,并且明年的书籍会随着结转而开启。

    Yves Reynhout经常谈论建模时间和日程安排; Evolving a Model可能是一个很好的起点。

答案 1 :(得分:7)

您确实需要快照的实例很少。但有一对 - 一个常见的例子是分类帐中的帐户。您将拥有数千或数百万的信用/借记事件,从而产生帐户的最终BALANCE状态 - 如果不经常快照快照,那将是疯狂的。

我在设计Aggregates.NET时设置快照的方法默认为关闭,并且要使您的聚合或实体必须继承AggregateWithMementoEntityWithMemento,而您的实体必须定义{ {1}},RestoreSnapshotTakeSnapshot

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

ShouldTakeSnapshot

每50个活动当然会拍摄一张快照。

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

至于商店 - 你可以使用任何东西。 VOU是正确的,虽然键值存储是最好的,因为你只需要1.检查是否存在2.加载整个东西 - 这是kv的理想选择

对于系统重启 - 我并没有真正关注你描述的问题。您的域服务器没有理由在其不同时间点执行不同的操作时具有状态。它应该只做一件事 - 处理下一个命令。在处理命令的过程中,它从事件存储中加载数据,包括快照,对生成业务异常的实体或记录到存储的域事件运行命令。

我认为你可能会试图通过缓存和冷启动的讨论来进行过多的优化。