【问题标题】:Understanding conflict resolution in firebase了解 Firebase 中的冲突解决方案
【发布时间】:2018-05-29 21:26:08
【问题描述】:

我正在尝试了解解决冲突在 firebase 中的工作方式,我需要一些帮助。

假设我已经将 json 对象保存在 firebase 的一个节点中 实时:

{
    "shape": "rectangle",
    "stroke": 10, 
    "color": "black"
}

我已经定义了一个测试页面,它可以读取这些数据并显示,还可以实时监听节点上发生的变化。我添加了一项更新数据的规定,最终仅更新特定的键值。

用例示例

client 1 - loads the page
    data - {"shape": "rectangle", "stroke": 10, "color": "black"}
client 2 - loads the page 
    data - {"shape": "rectangle", "stroke": 10, "color": "black"}

client 2 goes offline
client 2 updates stroke value to 20 
    data - {"shape": "rectangle", "stroke": 20, "color": "black"}
* data is yet to sync to the server

client 1 makes a change after client 2 has already done with its changes and changes stroke to 5
    data - {"shape": "rectangle", "stroke": 5, "color": "black"}
* data gets synced to the server immediately


client 2 comes online and pushes its changes and overrides the changes made by client 1
    data - {"shape": "rectangle", "stroke": 20, "color": "black"}

理想情况下,由于客户端 1 的更改晚于客户端 2,因此客户端 1 的更改应在客户端 2 数据同步时保留。

如果有人能建议我在 firebase 中解决这种类型的冲突,我会非常高兴(可能是通过定义一些规则和一些额外的逻辑)。

【问题讨论】:

  • 我将建议您为 JSON 对象添加时间戳,并使用安全规则确保写入的数据是最新的。但后来我在Firebase Reference 上看到ServerValue.TIMESTAMP 包含“由 Firebase 服务器确定的当前时间戳”。现在我想知道当client2推送到数据库的数据被写入时会显示什么时间。我会等待其他人回答。

标签: firebase firebase-realtime-database


【解决方案1】:

使用您当前的代码,预期的行为确实是最后一次写入获胜。

还有其他两种选择:

  1. 使用事务检测数据已更改,然后重试。
  2. 通过使用将写入与每个客户端分开的数据结构,完全防止冲突。

让我们依次看看。

使用事务是解决此问题的最常见方法。当您在 Firebase 中使用事务时,客户端会向服务器发送“比较和设置”操作。这是一个类型的指令:“如果当前值为 A,则将其设置为 B”。在您的场景中,这意味着第二次写入检测到笔画已经更改,因此会重试。

要了解有关交易的更多信息,请查看Firebase documentation,并在我的回答中了解它们的工作原理。

这听起来像是一个很好的解决方案,但不幸的是它确实会影响代码的可伸缩性。尝试修改相同数据的用户越多,事务重试的可能性就越大。这就是为什么最好考虑一下是否可以完全避免冲突。

预防冲突是目前最好的冲突解决策略。通过防止冲突,您永远不必解决它们,这意味着您不必编写代码来解决冲突,这意味着您的应用程序将扩展得更好/更远。

为防止发生冲突,您需要寻找一种数据结构,您的用户总是在其中写入独特的位置。在您的用例中,您可以让客户端将其“更新操作”写入更新队列,而不是让每个客户端更新笔画。例如:

shapes
  shapeid1
    pushid1: {"shape": "rectangle", "stroke": 10, "color": "black"} /* initial data */
    pushid2: { "stroke": 5 } /* first update */
    pushid3: { "stroke": 20 } /* second update */

在这种数据结构中,没有人会覆盖其他人的数据(这在安全规则中很容易实施)。每个人都只是在形状上添加新的更新(使用ref.push(),它会按时间顺序生成唯一的位置)。

要获取形状的当前数据,每个客户端都需要读取该形状的所有更新并在客户端上重新计算它们。对于我见过的大多数用例,这是一个简单的操作,但如果不是:让云函数计算状态的定期快照非常容易。

【讨论】:

  • 防止冲突的方法听起来很优雅。但是我怀疑这些事务是否会有所帮助,因为客户端可以在离线(未连接到服务器)时进行更改,并且客户端可能有也可能没有最新的。因此,我想知道是否有办法使用一些时间戳来改变同步发生的方式,以便最新的更新获胜。
  • 离线时事务确实无法工作。这是我更喜欢防止冲突的另一个原因:push() 生成保持有序的密钥,即使您处于离线状态。但总有机会“嗯?”用户重新上线后的那一刻。
  • 您可以做的一件事是在每次更新中设置一个客户端时间戳。然后,您的安全规则可以通过将时间戳与now 进行比较来拒绝它认为已过期的更新。
  • 即使定义了时间戳,我相信当用户在离线/在线时更新不同的字段时,定义规则会变得更加棘手和精细。例如,如果离线客户端 (c2) 更新颜色和笔画,而在线客户端 (c1) 仅更新笔画,那么冲突解决后我希望结果具有 c2 定义的颜色和 c1 定义的笔画。这样的用例让我认为事务无助于处理这种情况。我希望你同意这一点
  • 这完全取决于您如何定义“更新”以及如何定义“冲突”。如果您想允许您描述的用例,您应该将每个单独的属性更改存储为“更新”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-14
  • 1970-01-01
  • 2017-03-15
相关资源
最近更新 更多