Javascript Firebase事务错误
考虑以下几点:Javascript Firebase事务错误,javascript,firebase,firebase-realtime-database,firebase-queue,Javascript,Firebase,Firebase Realtime Database,Firebase Queue,考虑以下几点: function useCredits(userId, amount){ var userRef = firebase.database().ref().child('users').child(userId); userRef.transaction(function(user) { if (!user){ return user; } user.credits -= amount;
function useCredits(userId, amount){
var userRef = firebase.database().ref().child('users').child(userId);
userRef.transaction(function(user) {
if (!user){
return user;
}
user.credits -= amount;
return user;
}, NOOP, false);
}
function notifyUser(userId, message){
var notificationId = Math.random();
var userNotificationRef = firebase.database().ref().child('users').child(userId).child('notifications').child(notificationId);
userNotificationRef.transaction(function(notification) {
return message;
}, NOOP, false);
}
{
"notifications": {
"1": "notification 1",
"2": "notification 2"
}
}
这些都是从同一个节点js进程调用的
用户如下所示:
{
"name": 'Alex',
"age": 22,
"credits": 100,
"notifications": {
"1": "notification 1",
"2": "notification 2"
}
}
当我运行压力测试时,我注意到有时传递给userRef transaction update函数的用户对象不是完整用户,而是以下对象:
function useCredits(userId, amount){
var userRef = firebase.database().ref().child('users').child(userId);
userRef.transaction(function(user) {
if (!user){
return user;
}
user.credits -= amount;
return user;
}, NOOP, false);
}
function notifyUser(userId, message){
var notificationId = Math.random();
var userNotificationRef = firebase.database().ref().child('users').child(userId).child('notifications').child(notificationId);
userNotificationRef.transaction(function(notification) {
return message;
}, NOOP, false);
}
{
"notifications": {
"1": "notification 1",
"2": "notification 2"
}
}
这显然会导致错误,因为user.credits不存在
传递给userRef事务的update函数的用户对象与userNotificationRef事务的update函数返回的数据相同,这是可疑的
为什么会这样?如果我在用户父位置上运行这两个事务,这个问题就会消失,但这是一个不太理想的解决方案,因为我会有效地锁定并读取整个用户对象,这在添加一次写入通知时是多余的 根据我的经验,您不能依赖传递到事务更新函数的初始值。即使在数据存储中填充了数据,也可以使用
null
、部分值或过时的旧值(在飞行中进行本地更新的情况下)调用该函数。只要您在编写函数时采取防御方法(您应该这样做!),这通常不是问题,因为虚假更新将被拒绝,事务将重试
但是要注意:如果因为数据没有意义而中止事务(通过返回未定义的
),则不会对服务器进行检查,也不会重试。因此,我建议永远不要中止事务。我构建了一个透明地应用此修复(和其他修复)的模型;它只是一个浏览器,但可以简单地适应节点
另一件有帮助的事情是,在事务之前插入一个on('value')
调用同一个ref,并使其保持活动状态,直到事务完成。这通常会导致事务在第一次尝试时在正确的数据上运行,不会对带宽造成太大影响(因为无论如何都需要传输当前值),并且如果您已应用设置或默认设置为true
,则会稍微增加本地延迟。我在我的库中进行了很多优化和调整
最重要的是,在撰写本文时,SDK中仍然存在一个bug,错误的基值很少会被“卡住”,事务会不断重试(每隔一段时间就会失败maxretry
),直到重新启动进程
祝你好运!我仍然在服务器中使用事务,在服务器中可以很容易地重试故障,并且我有多个进程在运行,但我已经放弃在客户端上使用它们——它们太不可靠了。在我看来,重新设计数据结构通常更好,这样就不需要事务。我不知道事务会有这样的问题+1.您如何建议在不需要交易的情况下重新设计此“增值”操作?非常感谢您的详细回复-理解。交易似乎有些破裂——在我看来,客户不必担心所有这些因素。您如何建议在不使用firebase中的交易的情况下对银行账户余额进行建模?如果没有一个健壮的方法来处理这个事务,那么我觉得这是一个严重的限制。另一个问题是能够以事务方式更新某些值,同时以原子方式更新其他位置。这都是来之不易的经验,强调的是努力。只要记住上述注意事项,事务处理对于简单的增量可能是好的。另一种方法是将“+N”项推送到日志中,并让服务器使用事务定期汇总它们,这应该更健壮,最终也更一致。客户端可以读取最后一个聚合值并记录日志,以便动态计算当前值。深度原子更新是可能的,但不能是事务性的——同样,基于日志的设计是您的朋友。非常感谢您的评论。