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”项推送到日志中,并让服务器使用事务定期汇总它们,这应该更健壮,最终也更一致。客户端可以读取最后一个聚合值并记录日志,以便动态计算当前值。深度原子更新是可能的,但不能是事务性的——同样,基于日志的设计是您的朋友。非常感谢您的评论。