活动采购 - 活动回放

时间:2016-11-22 13:01:29

标签: cqrs event-sourcing event-store

我一直在阅读有关事件采购模式的内容,如果您想重建系统,这可能非常有用。但是,如果我需要在处理新传入请求时运行事件重建,该怎么办?该场景是否有任何特定模式或最佳实践? 因此,如何确保新的传入请求在重放时不会搞砸我的系统,而不是安排系统停机时间,因为事件同步和序列对我的系统非常重要。它涉及更新依赖于事件序列的DB记录。有什么想法吗?

4 个答案:

答案 0 :(得分:1)

我在类似情况下使用了CQRS + ES。 我使用准备好的数据创建了投影,我只能更新,但不能重建。 在每个查询中,我都会快速建立结果信息。

如果你需要执行一些长操作(比如db中的update),请使用sagas。生成事件 - >传奇 - >在saga结束并生成event2之后更新投影。

当然,事件收入和预测更新之间会有一些延迟。

了解更多关于你的系统以及这种变体对你来说是否足够好非常有趣。

答案 1 :(得分:1)

注意:在本示例中,所有ID都是6个随机字母数字,例如,它们可能是UUID或sha1。

如果您有以下事件:

WriteIndex | EventId | Type             | streamId | Data    
-------------------------------------------------------------------------------
1          | qcwbf2  | car.created      | hrxs21   | { by: Alice, color: blue }
2          | e1owui  | car.repainted    | hrxs21   | { color: red }
3          | fjr4io  | car.created      | tye24p   | { by: Alice, color: blue }
4          | fhreui  | customer.created | b2dhuw   | { name: Bob }
5          | urioe7  | car.sold         | hrxs21   | { to: b2dhuw }
6          | fhreui  | customer.renamed | b2dhuw   | { name: Charlie }
-------------------------------------------------------------------------------

在您的预测中(6点之后):

CarId  | Creator | Color | Sold | Customer | CustomerId
-------------------------------------------------------
hrxs21 | Alice   | red   | yes  | Bob      | b2dhuw
tye24p | Alice   | blue  | no   |          | 
-------------------------------------------------------

CustomerId | Name
---------------------
b2dhuw     | Charlie
---------------------

请注意,您的汽车投影错误,因为您没有正确地在汽车投影仪中收听“ customer.rename”。

您重写了所有投影机,并想重播。

你的恐惧

您重播事件并转到此:

CarId  | Creator | Color | Sold | Customer | CustomerId
-------------------------------------------------------
hrxs21 | Alice   | red   | yes  | Charlie  | b2dhuw
tye24p | Alice   | blue  | no   |          | 
-------------------------------------------------------

但与此同时,在“重建汽车缓存”(投影不过是缓存)之外,有两个新事件出现:

WriteIndex | EventId | Type             | streamId | Data    
-------------------------------------------------------------------------------
7          | je3i32  | car.repainted    | hrxs21   | { color: orange }
8          | 8c227x  | customer.created | wuionx   | { name: Dan }
9          | e39jc2  | car.sold         | tye24p   | { to: wuionx }

所以它“似乎”认为新的重建的新鲜缓存永远不会“恢复到当前状态”,因为现在查理(前鲍勃)的汽车现在是橙色而不是红色,已经创建了新客户,并且汽车号wuionx是现在归Dan所有。

您的解决方案

  1. 使用“索引”(警告,需要仔细设计)或“时间戳记”(警告,将校正事件注入到过去的日期中)标记缓存数据!
  2. 读取缓存时,请确保“应用”“最新更改”,以待应用到SYNC模式(不是ASYNC)。

原因:重建数千个事件可能需要一些时间。但是,仅重建几十个就可以快速点亮。

Quick-n-Dirty解决方案

所以...让您的“重播器”改为具有这些表:(我假设WriteIndex对于启发是可靠的,但实际上,我可能会使用writeIndex以外的其他东西,这仅是出于说明目的) :

CarId  | Creator | Color | Sold | Customer | CustomerId | LatestChangedBy
--------------------------------------------------------|----------------
hrxs21 | Alice   | red   | yes  | Charlie  | b2dhuw     | 6
tye24p | Alice   | blue  | no   |          |            | 3
-------------------------------------------------------------------------

因此,当要“消费”汽车tye24p时,您会看到它的最新更新是由于3而完成的,因此您只能在此聚合上“重播” 4端监听,因此您将得到以下结果:

CarId  | Creator | Color | Sold | Customer | CustomerId | LatestChangedBy
--------------------------------------------------------|----------------
hrxs21 | Alice   | red   | yes  | Charlie  | b2dhuw     | 6
tye24p | Alice   | blue  | yes  | Dan      | wuionx     | 9
-------------------------------------------------------------------------

这是无效的,因为您已经在重播4至6时再次“重播”。

更好的解决方案

拥有一个全局重播计数器

CarId  | Creator | Color | Sold | Customer | CustomerId
-------------------------------------------------------
hrxs21 | Alice   | red   | yes  | Charlie  | b2dhuw
tye24p | Alice   | blue  | no   |          | 
-------------------------------------------------------

ReplayerMetaData
-------------------------------------------------------
lastReplayed: 6
-------------------------------------------------------

当您要访问任何内容时,请对“有待重播的任何新事件”进行SYNC“快速更新”。

如果您要访问car tye24p,您只会看到直到“索引9”的事件,然后重放了多达6的事件。然后,在“读取之前”强制“更新所有未决”并仅重放7、8和9.您最终得到了这个汽车缓存表:

CarId  | Creator | Color  | Sold | Customer | CustomerId
--------------------------------------------------------
hrxs21 | Alice   | orange | yes  | Charlie  | b2dhuw
tye24p | Alice   | blue   | yes  | Dan      | wuionx
-------------------------------------------------------

ReplayerMetaData
-------------------------------------------------------
lastReplayed: 9
-------------------------------------------------------

总体

使用此解决方案,您:

  • 在切换读取模型之前,可以进行无数次尝试。
  • 在进行所有试验时,请保持“在线”状态。
  • 当您的系统“准备就绪”以进行最终重建时(假设处理1百万个事件需要1个小时),您只需在系统在线的情况下运行它即可。
  • 大型重建后,说出lastReplayed = 1.000.000。如果在那一小时内出现了2.000个新事件,则可以再次“重播那些最新事件”。
  • 想象一下,那2.000需要5分钟,您的指针现在是1.002.000。想象一下,在这5分钟内,又有300个事件发生:
  • 重播“仅那些最新的”,您的指针将是:1.002.300
  • 一旦您“几乎赶上了”(无论是否还有50个事件的差距),您只需将读取模型(仅通过配置标志)切换到新模型=>这意味着您 进行全面部署,您必须已经部署了能够从此处或从此处读取的版本,因此切换是“立即的”。
  • 切换后,您只需“确保”您的读取中“强制同步应用最新消息”即可。
  • 这将仅影响第一次读取,最多应为1或2秒...下一次读取很可能已经同步,因此继续检查间隙是否对性能没有任何影响,可以这样说。 “您需要再更新0个事件”并完成。

希望有帮助!

答案 2 :(得分:0)

根据您所描述的限制,听起来像 live 从零重建不能成为您计划的一部分。您可以改为进行A / B设置,在当时处于脱机状态的新系统上播放事件,然后在新系统赶上后切换到新系统。关键是旧系统和新系统都可以同时收听事件流。

如果您订阅了各种事件子集的系统,则可能只需要为其中一个子系统重播事件,并且仍然可以在没有该子系统的情况下满足您的同步/序列需求。

您可以通过在数据中包含事件序列号并使序列相关服务延迟处理(如果尚未看到该事件)来阻止对过时信息采取行动。

答案 3 :(得分:0)

要将事件投影到读取模型,您需要使用某种追赶订阅,例如EventStore提供的订阅。在这种情况下,您的新活动将保存到商店,但不会立即投影。

但是,您必须意识到您的用户将开始查看严重过时的数据并根据不一致的读取模型执行操作。我宁愿避免这种情况,让系统重建。

我同意第一个答案,你可能想要同时建立一个新的阅读模型,更新旧的并在某个时候进行切换,甚至可能不是所有用户都是。