Firebase 中非常接近的事件的数据一致性

Data Consistency on very close events in Firebase

我正在构建一个 iOS 游戏。每个游戏室由两个用户构成。两个用户匹配后,我在两台设备上都显示一个 "waiting for respond" 计时器:他们需要在 8 秒内单击 "I'm Ready" 按钮,否则他们都将被踢出房间 + 从 Firebase 中删除。

用户的正确状态(在他们每个人点击 "I'm Ready Button" 之前):

Parent
      Matches
             User 1
                   opponent : User2
                   state : "NotReady"
             User 2
                   opponent : User1
                   state : "NotReady"

重要说明 - 两台设备上的计时器时差为 +-2 秒,换句话说 - 一旦设备计时器将在另一个设备之前结束

当用户按下 "I'm Ready" 按钮时,我正在更新 state : "userReady",并检查其他用户是否也准备好了(观察其 state 值)。

如果两个用户都用户就绪 - 游戏开始。

问题

所以我们已经清楚,在 100% 的情况下,我的两个设备之间的时间差异很小。 但是,例如,

User1 单击了 I'm Ready 按钮。所以现在 User2 得到了一个 ChildUpdate 事件,据他所知 - User2 已经完全准备好比赛了。

User1 timer 首先结束(事实),所以当他的计时器结束时,User2 timer 将保持 1 秒。 现在,User1 的时间刚刚达到零,所以他被踢出房间,并在每个用户节点上发送一个 removeValue 事件。当这发生时,在这个非常小的 "gap",(在 User1 timer 结束的零时间之间,User2 时钟显示的 1 秒(事实)- 他 按下 就绪按钮。比他认为 User1 已经准备好开始玩,而且他也 - 比游戏开始玩。

最终结果 -

两名玩家都已从 Firebase 中删除

User1 不在房间

用户2开始游戏(他认为自己是对手)

我该如何解决这种最终情况?,我已经尝试过仅在 "UpdateChild state" 完成后才调用 startGame 函数,但是它仍然进入,可能是因为 updateChild 和 removeValue 的顺序"?

有什么建议吗?非常感谢 Firebase 团队一直以来的努力!!!

用户 1 接受然后过期是没有意义的。用户 2 应该是那个到期的,如果达到了时间限制,他还没有接受。

为防止这种情况发生,您正在寻找 transactions。当您 "expire" 一个用户时,使用一个事务来这样做,这样就不会有数据冲突。

var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");

ref.child('Parent/Matches').child(user1).transaction(function(currentValue) {
    if( currentValue && currentValue.state === 'NotReady' ) {
       return null; // delete the user
    }
    else {
       return undefined; // abort the transaction; status changed while we were attempting to remove it
    }
});

在 swift 中可能是正确的:

var ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/Parent/Matches/<user id>")

upvotesRef.runTransactionBlock({
    (currentData:FMutableData!) in
    if currentData && currentData.value["state"] != "NotReady" {
        return FTransactionResult.successWithValue(NSNull)
    }
    return FTransactionResult.abort();
});

此外,您可以通过不deleting/expiring 记录直到两个用户都达到他们的接受时间限制,然后在事务中同时执行这两个操作来简化事情并减少混乱的机会。