2016-08-02 140 views
1

考虑以下几点:火力地堡交易错误

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); 
} 

这些都是从同一个节点JS过程调用。

用户看起来是这样的:

{ 
    "name": 'Alex', 
    "age": 22, 
    "credits": 100, 
    "notifications": { 
    "1": "notification 1", 
    "2": "notification 2" 
    } 
} 

当我跑我的压力测试中,我注意到,有时传递给userRef交易更新功能的用户对象是不是只有以下完整的用户:

{ 

    "notifications": { 
    "1": "notification 1", 
    "2": "notification 2" 
    } 
} 

这显然会导致错误,因为user.credits不存在。

可疑传递给userRef事务的update函数的用户对象与userNotificationRef事务的更新函数返回的数据相同。

为什么会出现这种情况?如果我在用户父位置上运行这两个事务,这个问题就会消失,但这是一个不太理想的解决方案,因为我然后有效地锁定并读取了整个用户对象,这在添加一次写入通知时是多余的。

回答

3

根据我的经验,您不能依赖传递到事务更新函数的初始值。即使数据存储在数据存储区中,也可以使用null(部分值)或过时的旧值(在飞行中进行本地更新的情况下)调用该函数。只要您在编写函数时采取防御措施(您应该!),这通常不是问题,因为虚假更新将被拒绝并且事务重试。

但要注意:如果你中止交易(通过返回undefined),因为该数据没有意义,那么它是不是服务器签文件,也不会重试。出于这个原因,我建议不要放弃交易。我建立了一个monkey patch来透明地应用此修补程序(和其他);它仅适用于浏览器,但可以适用于节点。

您可以做的另一件事是帮助您在事务处理之前在同一个ref上插入一个on('value')调用,并保持它处于活动状态直到事务完成。这将使通常导致事务在第一次尝试时在正确的数据上运行,不会对带宽造成太大影响(因为无论如何都需要传输当前值),并且如果设置了applyLocally,则会增加本地延迟或者默认为true。我的NodeFire库中,我do this,以及许多其他优化和调整。

除此之外,截至撰写本文时,SDK中仍然存在一个错误,很少有错误的基值会“卡住”,并且事务会连续重试(每隔一段时间连续重试),直到重新启动该过程。

祝你好运!我仍然在我的服务器中使用事务,其中失败可以轻松地重试,并且我有多个进程正在运行,但已经放弃在客户端上使用它们 - 它们太不可靠了。在我看来,重新设计数据结构往往更好,因此不需要事务处理。

+0

我不知道交易有这样的问题...... +1。你如何建议重新设计这个“增值”操作而不需要交易? – qxz

+0

非常感谢您的详细回复 - 理解。看起来交易有些失败 - 在我看来,客户不应该担心所有这些考虑因素。您如何建议在不使用Firebase交易的情况下对银行账户余额进行建模?如果没有一个强大的方法来处理这个使用交易,那么我觉得这是一个严重的限制。另一个问题是能够在事务上更新某个值,并同时自动更新其他位置。 – user3391835

+0

这是所有来之不易的经历,重点在*难*。只要你牢记上述注意事项,事务对于简单的增量可能就不错了。另一种方法是将“+ N”条目推送到日志中,并让服务器定期使用事务处理它们,这应该更加健壮并且最终保持一致。客户端可以读取最后的聚合值并记录,以便即时计算当前值。深度原子更新是可能的,但不能是事务性的 - 再次,基于日志的设计是您的朋友。 – Piotr