例如:
如果用户提交收据,则必须更新收货对象以及发票对象以及其他总帐对象.
如果更新已开始但用户中途失去了互联网连接,则应回滚所有更改.
在Firebase中实现这一目标的最佳方法是什么?
解决方法
你需要这个吗?
通常,如果符合以下条件,则不需要此:
>你不是用高并发性写的(DIFFERENT用户每分钟写入数百次写入操作)
>你的依赖是直截了当的(B取决于A,C取决于A,但A不依赖于B或C)
>您的数据可以合并为一个路径
开发人员对他们的数据中出现的孤立记录有点过于担心.
Web套接字在一次写入和另一次写入之间失败的可能性可能微不足道,并且在某些地方之间发生冲突
基于时间戳的ID.这并不是说这是不可能的,但它通常是低后果,极不可能,并且不应该是你的主要关注点.
此外,使用脚本甚至只需在JS控制台中输入几行代码即可轻松清理孤儿.再一次,
它们的后果往往很低.
你可以做什么而不是这个?
将必须以原子方式写入的所有数据放入单个路径中.然后,如果需要,您可以将其写为单个set或transaction.
或者在一个记录是主记录而其他记录依赖于此的情况下,只需先写入主记录,然后在回调中写入其他记录.添加安全规则以强制执行此操作,以便在允许其他人写入之前始终存在主记录.
如果要简化数据的规范化以简化和快速迭代(例如,获取用户的名称列表),那么只需在单独的路径中索引该数据.
然后,您可以在单个路径中获取完整的数据记录,并在快速查询/排序友好列表中包含名称,电子邮件等.
什么时候有用?
如果您有一组非规范化的记录,则可以使用此工具:
>实际上不能将实际合并到一条路径中
>具有复杂的依赖关系(A取决于C,C取决于B,B取决于A)
>记录以高并发性写入(即,每分钟可能有数百个写操作由不同用户的SAME记录)
你怎么做到这一点?
我们的想法是使用更新计数器来确保所有路径都保持相同的版本.
1)创建一个使用事务递增的更新计数器:
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)提供记录安全规则以强制执行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写入数据
由于您具有安全规则,因此只有在计数器不移动时,记录才能成功写入.如果它确实移动,则记录已被并发更改覆盖,因此它们不再重要(它们不再是最新且最好的).
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)回滚
回滚涉及更多,但可以建立这个原则:
>在调用set之前存储旧值
>监控每组操作失败
>在任何已提交的更改中设置回旧值,但保留新计数器
预建的图书馆
我今天写了一篇文章来做这个和stuffed it on GitHub.随意使用它,但请确保你没有通过阅读“你需要这个吗?”让你的生活变得复杂.以上.