Firebase中非常接近的事件的数据一致性 [英] Data Consistency on very close events in Firebase

查看:146
本文介绍了Firebase中非常接近的事件的数据一致性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个iOS游戏。每个游戏室由两个用户构成。在两个用户匹配后,我在两台设备上都显示一个等待响应计时器:他们需要在8秒钟内点击我准备好按钮,否则两个人都将被踢出房间并从Firebase中删除。
$ b

用户的正确状态(每个用户点击我准备就绪按钮之前):

 父母
匹配
用户1
对手:用户2
状态:NotReady
用户2
对手:User1
state:NotReady

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

当用户按下我准备就绪按钮我正在更新状态:userReady,并检查其他用户是否准备就绪(观察其状态 value)。



如果两个用户都是 userReady - 游戏开启。

问题



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

User1 点击 I准备好按钮。所以现在 User2 得到了一个 ChildUpdate 事件,据他所知 - User2 <
$ b $ 用户计时器最先结束(事实),所以当他的计时器将完成, User2计时器将保持 1 秒。 现在 User1 时间刚刚达到零,所以他被踢出房间,并发送 removeValue
code>事件在每个用户节点上。发生这种情况时,在这个非常小的差距( User1定时器结尾的零时间, User2 时钟显示的时间为1秒(事实),而他按下按钮,他认为 User1 已准备好播放,比赛开始。



最终结果 -

这两名玩家都从Firebase中删除用户2离开房间



User2开始游戏(他认为他是对手)

我该如何解决这种情况?已经尝试只在UpdateChild状态完成时调用 startGame 函数,但它仍然进入,可能是因为updateChild和removeValue的顺序?



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

解决方案

U没有意义ser 1会接受,然后无论如何到期。用户2应该是一个过期,如果时间限制已经达到,他还没有接受。

为了防止这种情况,你正在寻找交易。当你过期的用户,使用一个事务来这样做,所以没有数据冲突。

  var ref =新的Firebase(https://< YOUR-FIREBASE-APP> .firebaseio.com /); 
$ b $ ref.child('Parent / Matches')。child(user1).transaction(function(currentValue){
if(currentValue&& currentValue.state ==='NotReady '){
return null; //删除用户
}
else {
return undefined; //中止事务;状态在我们试图删除的时候改变了
}
});

可能在swift中正确:

<$ p $
$ b var ref = Firebase(url:https://< YOUR-FIREBASE-APP> .firebaseio.com / Parent / Matches /< user id>) $ b upvotesRef.runTransactionBlock({
(currentData:FMutableData!)in
if currentData&&; currentData.value [state]!=NotReady{
return FTransactionResult.successWithValue (NSNull)
}
return FTransactionResult.abort();
});此外,您可以通过不删除/终止记录,直到两者都可以简化的事情,减少混乱的机会用户已经达到了他们的接受时间限制,然后在一次交易中同时进行。

I am building an iOS game. Each game room is constructed from two users. After two users get matched, I'm displaying a "waiting for respond" timer on both devices: they need to click am "I'm Ready" button within 8 second or both of them will get kicked from the room + deleted from Firebase.

The correct state of the users (before each of them clicked on the "I'm Ready Button"):

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

Critical note - The timer on both devices time difference is +-2 seconds, In other words - once device timer is going to end before the other one

When a user press the "I'm Ready" button i'm updating the state : "userReady",and check if the other user is ready too (observing its state value).

If both users are userReady - game on.

PROBLEM

so we have already cleared that in 100% of the cases i have a small time difference between both devices. BUT, if for instance ,

User1 clicked I'm Ready button. So now User2 got an ChildUpdate event, and as far as he knows - User2 is completely ready to play.

User1 timer is ending first(fact), so when his timer will finish, User2 timer will remain 1 seconds. NOW, on User1 time just reached zero, so he get kicked out of the room, and send a removeValue event on each of the Users nodes. While this is happening, at this very small "gap",(between the zero time of User1 timer ending,User2 clock show's 1 sec(fact) - and he press the ready button. than he thinks that User1 is ready to play, and he as well - than the game starts playing.

Final Results -

Both players got deleted from Firebase

User1 is out of the room

User2 starts the game(and he think he is an opponent)

How can i solve this end-case scenario?, I have already tried calling startGame function only when the "UpdateChild state" is done, but it still gets in, maybe because the order for updateChild,and removeValue"?

Any suggestions? And BIG thank you Firebase team for reaching how all the time!!!

解决方案

It doesn't make sense that User 1 would accept and then expire anyway. User 2 should be the one expiring if the time limit is reached and he hasn't accepted yet.

To prevent this, you're looking for transactions. When you would "expire" a user, use a transaction to do so, so that there are no data conflicts.

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

Possibly correct in 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();
});

Furthermore, you could simplify things and reduce the opportunity for chaos by not deleting/expiring the records until both users have reached their accept time limit, and then doing both at once in a transaction.

这篇关于Firebase中非常接近的事件的数据一致性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆