38

Firebase 允许以事务方式更新资源。据我了解,客户端这样做会向服务器发送请求,说“如果旧值是 X,则将新值设为 Y”。如果存在争用,服务器可能会拒绝来自客户端的多个更新,直到一个被接受为止。

现在,如果我想以原子方式更新多个资源怎么办?

如果接受第一个更新,然后客户端在接受第二个更新之前断开连接,会发生什么情况。有没有办法在原子事务中包含多个更新?如果没有,这个问题是否有惯用的解决方案?

4

2 回答 2

44

更新

现在可以自动更新多个位置。有关详细信息,请参阅此博客文章

var mergedUpdate = {};
mergedUpdate[ 'users/' + userId + '/widgets/' + widgetId ] = true;
mergedUpdate[ 'widgets/' + widgetId ] = widgetData;

var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
ref.update(mergedUpdate);

这不会强制执行事务数据(如果当前值为 X,则设为 Y),但这部分可以移至安全规则。例如,如果我们想同时更新两个计数器,我们可以添加如下规则:

{
  "counter1": {
     ".validate": "newData.val() === (data.val()||0)+1"
  },

  "counter2"1 {
     ".validate": "newData.val() === (data.val()||0)+1"
  }
}

现在我们可以尝试与上面相同的多路径更新。如果自从我们上次从服务器读取它们后这些值发生了变化,则尝试将失败。我们可以检查if( error.code === 'PERMISSION_DENIED' ) { ... }失败是否是由于验证,并相应地重试。

原帖

唯一的方法是在一个共同的祖先上运行一个事务。

例如,如果您想更新 /a/b/c 和 /a/x/y,您可以在 /a 处运行事务并更改这两个值。

这种方法的缺点是网络 I/O 可能很昂贵,因为需要下载事务中的所有数据,然后将其发送回服务器。

您可能要考虑的一种更复杂但可能更强大的方法是重组您的数据,以便您存储编辑历史,而不是存储实际值。例如,如果您要存储银行余额信息,则可以存储存款和取款的历史记录。然后当你想得到余额时,你会回放整个历史并计算最终余额。

这种方法的美妙之处在于它可以让您进行原子更新。例如,如果您将资金从账户 A 转移到账户 B,您只需在日志末尾附加一个元素,说明“从账户 A 转移到账户 B N 美元”。附加该单个元素是原子操作。

这是我们使用Firepad(我们的协作式文本编辑器)所采用的方法。

于 2013-07-03T00:43:16.627 回答
0

还有另一种方法……非常乏味……而且不是真正的交易……

由于 Firebase 允许对值进行原子更改,因此您可以创建一个锁或信号量,其含义是“在资源 x、y 和 z 上启动事务”......当然,它不是真正的事务,因为它没有块资源,一部分来自锁。我们可以按照惯例将其称为事务。客户端需要知道当锁被占用时,他们不应该改变x、y和z资源......

于 2015-04-29T22:17:24.217 回答