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 记录直到两个用户都达到他们的接受时间限制,然后在事务中同时执行这两个操作来简化事情并减少混乱的机会。
我正在构建一个 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 记录直到两个用户都达到他们的接受时间限制,然后在事务中同时执行这两个操作来简化事情并减少混乱的机会。