针对复杂写入的Firebase提交/回滚

我正在使用Firebase撰写财务应用程序,要提交收据,还需要更新许多其他对象。 为使数据有效,所有数据更新都需要成功完成。 如果其中一个写入发生错误,则必须回滚所有更新。

例如:

如果用户提交收据,则必须更新收据对象以及发票对象以及其他总帐对象。

如果更新开始,但用户中途丢失了互联网连接,则所有更改都应回滚。

在Firebase中实现此目的的最佳方式是什么?

首先,让我们聊聊为什么有人可能想在多个数据path上执行提交/回滚…

你需要这个吗?

一般来说,你不需要这个,如果:

  • 你不是写高并发(不同的用户每分钟写同一个logging数百)
  • 你的依赖是直接的(B依赖于A,而C依赖于A,但A不依赖于B或C)
  • 您的数据可以合并到一个单一的path

开发人员对数据中出现的孤立logging感到有点担心。 在一次写入和另一次写入之间出现web套接字失败的可能性可能是微不足道的,而且在基于时间戳的ID之间的冲突顺序上也是如此。 这并不是说这是不可能的,但是这通常是不太可能的,而且不应该成为你的主要关注点。

此外,孤儿是非常容易清理的脚本,甚至只需要在JS控制台input几行代码。 所以,他们往往是非常低的后果。

你能做什么而不是这个?

将所有必须以primefaces方式写入的数据放入单个path。 那么你可以把它写成一个单一的集合或一个事务,如果有必要的话。

或者在一个logging是主要logging并且其他logging取决于此的情况下,只需首先写入主logging,然后在回写中写入其他logging。 添加安全规则来强制执行此操作,以便在允许其他人写入之前始终存在主logging。

如果为了简化迭代(例如,获取用户名称列表)而对数据进行非规范化处理,那么只需在单独的path中对数据进行索引即可。 然后,您可以在一个快速查询/sorting列表中将完整的数据logging保存在单个path中,并input名称,电子邮件等。

什么时候这有用?

如果您有非规范化的一组logging,这是一个适当的工具:

  • 实际上不可能实际上合并成一条path
  • 有复杂的依赖关系(A依赖于C,C依赖于B,而B依赖于A)
  • logging写入的并发性较高(即,不同用户每分钟可能有数百个写操作到同一logging)

你怎么做到这一点?

这个想法是使用更新计数器来确保所有的path保持在同一修订版本。

1)创build一个使用事务递增的更新计数器:

function updateCounter(counterRef, next) { counterRef.transaction(function(current_value) { return (current_value||0)+1; }, function(err, committed, ss) { if( err ) console.error(err) else if( committed ) next(ss.val()); }, false); } 

2)给它一些安全规则

 "counters": { "$counter": { ".read": true, ".write": "newData.isNumber() && ( (!data.exists() && newData.val() === 1) || newData.val() === data.val() + 1 )" } }, 

3)给你的logging安全规则来执行update_counter

 "$atomic_path": { ".read": true, // .validate allows these records to be deleted, use .write to prevent deletions ".validate": "newData.hasChildren(['update_counter', 'update_key']) && root.child('counters/'+newData.child('update_key').val()).val() === newData.child('update_counter').val()", "update_counter": { ".validate": "newData.isNumber()" }, "update_key": { ".validate": "newData.isString()" } } 

4)用update_counter写入数据

由于您有安全规则,只有计数器不移动,logging才能成功写入。 如果它确实移动了,那么这些logging就被一个并发的改变覆盖了,所以它们不再重要(它们不再是最新和最伟大的)。

 var fb = new Firebase(URL); updateCounter(function(newCounter) { var data = { foo: 'bar', update_counter: newCounter, update_key: 'myKey' }; fb.child('pathA').set(data); fb.child('pathB').set(/* some other data */); // depending on your use case, you may want transactions here // to check data state before write, but they aren't strictly necessary }); 

5)回滚

回滚有一点涉及,但可以build立这个原则:

  • 在调用set之前存储旧的值
  • 监视每一组故障
  • 在任何已提交的更改中设置回旧值,但保留新的计数器

预build库

我今天写了一个lib, 把它塞到GitHub上 。 随意使用它,但请确保你没有通过阅读“你需要这个吗? 以上。