使用EventSourcing(NodeJS,MongoDB,JSON)跨多个偶尔连接的客户端同步数据

Jos*_*der 10 javascript synchronization mongodb node.js event-sourcing

我在服务器和多个客户端之间实现数据同步时遇到问题.我读到了关于事件采购的内容,我想用它来完成同步部分.

我知道这不是一个技术问题,而是一个概念问题.

我只是将所有事件发送到服务器,但客户端设计为不时脱机使用.

这是基本概念:视觉概念

服务器存储每个客户端都应该知道,它不会重播这些事件来服务于数据,因为主要目的是同步的客户端之间的事件,使他们能够在本地重播所有事件的所有事件.

客户有它一个JSON店,也使所有的事件,并重建从存储/同步事件的所有不同的集合.

由于客户端可以脱机修改数据,因此具有一致的同步周期并不重要.考虑到这一点,服务器应该在合并不同事件时处理冲突,并在发生冲突时询问特定用户.

因此,对我来说主要的问题是确定客户端和服务器之间的差异,以避免将所有事件发送到服务器.我也遇到了同步过程的顺序问题:首先推送更改,首先提取更改?

我目前构建的是服务器端上的默认MongoDB实现,它在所有查询中隔离特定用户组的所有文档(目前仅处理身份验证和服务器端数据库工作).在客户端上,我构建了一个围绕NeDB存储的包装器,使我能够拦截所有查询操作,以创建和管理每个查询的事件,同时保持默认查询行为不变.我还通过实现客户端生成的自定义ID并且是文档数据的一部分来补偿neDB和MongoDB的不同ID系统,因此重新创建数据库不会弄乱ID(同步时,这些ID应该在所有客户中保持一致).

事件格式如下所示:

{
   type: 'create/update/remove',
   collection: 'CollectionIdentifier',
   target: ?ID, //The global custom ID of the document updated
   data: {}, //The inserted/updated data
   timestamp: '',
   creator: //Some way to identify the author of the change
}
Run Code Online (Sandbox Code Playgroud)

为了在客户端上节省一些内存,我将在特定数量的事件中创建快照,以便完全重放所有事件将更有效.

因此,要缩小问题范围:我能够在客户端重放事件,我还能够在客户端和服务器端创建和维护事件,在服务器端合并事件也应该不是问题.使用现有工具复制整个数据库不是一种选择,因为我只是同步数据库的某些部分(甚至不是整个集合,因为文档被分配了它们应该同步的不同组).

但我遇到的麻烦是:

  • 确定在同步时从客户端发送什么事件的过程(避免发送重复事件,甚至所有事件)
  • 确定什么事件发送回客户端(避免发送重复的事件,甚至所有事件)
  • 同步事件的正确顺序(推/拉变化)

我想问的另一个问题是,将更新直接存储在类似修订版的文档中是否更有效?

如果我的问题不清楚,重复(我发现了一些问题,但他们在我的情况下没有帮助我)或者缺少某些东西,请发表评论,我会尽可能地保持它,以保持简单,就像我一样只写下一切可以帮助你理解这个概念.

提前致谢!

Nod*_*ode 6

这是一个非常复杂的主题,但我会尝试某种形式的答案。

看到您的图表后,我的第一反应是考虑分布式数据库如何在它们之间复制数据并在一个节点发生故障时恢复。这通常是通过八卦来完成的。

八卦回合可确保数据保持同步。带时间戳的修订保留在两端,按需合并,例如当节点重新连接时,或者只是在给定的时间间隔(通过套接字等发布批量更新)。

Cassandra 或 Scylla 等数据库引擎每轮合并使用 3 条消息。

示范:

节点A中的数据

{ id: 1, timestamp: 10, data: { foo: '84' } }
{ id: 2, timestamp: 12, data: { foo: '23' } }
{ id: 3, timestamp: 12, data: { foo: '22' } }
Run Code Online (Sandbox Code Playgroud)

节点B中的数据

{ id: 1, timestamp: 11, data: { foo: '50' } }
{ id: 2, timestamp: 11, data: { foo: '31' } }
{ id: 3, timestamp: 8, data: { foo: '32' } }
Run Code Online (Sandbox Code Playgroud)

第 1 步:同步

它列出了所有文档的 ID 和最后更新插入时间戳(可以随意更改这些数据包的结构,这里我使用详细的 JSON 来更好地说明该过程)

Node A -> Node B

[ { id: 1, timestamp: 10 }, { id: 2, timestamp: 12 }, { id: 3, timestamp: 12 } ]
Run Code Online (Sandbox Code Playgroud)

第 2 步:确认

收到此数据包后,节点 B 将接收到的时间戳与自己的时间戳进行比较。对于每个文档,如果它的时间戳较旧,则只需将其放入 ACK 负载中,如果较新,则将其与数据一起放置。如果时间戳相同,则不执行任何操作 - 显然。

Node B -> Node A

[ { id: 1, timestamp: 11, data: { foo: '50' } }, { id: 2, timestamp: 11 }, { id: 3, timestamp: 8 } ]
Run Code Online (Sandbox Code Playgroud)

步骤 3:ACK2

如果提供了 ACK 数据,节点 A 会更新其文档,然后对于未提供 ACK 数据的节点,将最新数据发送回节点 B。

Node A -> Node B

[ { id: 2, timestamp: 12, data: { foo: '23' } }, { id: 3, timestamp: 12, data: { foo: '22' } } ]
Run Code Online (Sandbox Code Playgroud)

这样,两个节点现在都可以双向合并最新数据(以防客户端进行离线工作) - 无需发送所有文档。

就您而言,您的事实来源是您的服务器,但您可以使用 WebRTC 等轻松地在客户端之间实现点对点八卦。

希望这能在某种程度上有所帮助。

卡桑德拉培训视频

青蟹解释