FireBase – 维护/保证数据的一致性

我试图了解什么是这种情况下正确的方法:

多人游戏,每个游戏only两个玩家结构。 每场比赛将完全随机化

让我们假设5个用户同时“login”到我的应用程序中,每个用户都在search一个匹配项。 每个用户都拥有一个名为“ opponent的属性,它等于对手的uniqueID (初始值等于""

假定用户1与用户3匹配。用户1将他自己的oppoent值更新为用户3的oppoent并且将对用户3 执行相同的操作

问题

1)如果同时用户2试图对用户3做同样的事情呢? 2)如果用户3同时尝试向用户4这么做?

要点

是否有可能“locking”用户值? 或冻结他们,一旦他们改变了? 我会采取错误的做法吗?

我想使用Security RulesValidation ,以创build一致性,但我可能会select错误的技术(FireBase)。 有什么想法吗?

编辑

我已经尝试过的安全规则,这仍然是由于某种原因,使第三个设备更改“已经改变的对手”的价值。

 { "rules": { ".read": true, ".write": true, "Users" : { "$uid" : { "opponent" : { ".write" : "data.val() == 'empty' || data.val() == null", ".validate": "data.val() == null || data.val() == 'empty' || newData.parent().parent().child(newData.val()) .child('opponent').val() == $uid" } ,".indexOn": "state" } } } } 

您可以使用Firebase安全规则validation许多事情。

例如,你可以说只有在对手没有对手的情况下才能写对手:

 "users": { "$uid": { "opponent: { ".write": "!data.exists()" } } } 

有了这个和以下操作:

 ref.child('users').child(auth.uid).child('opponent').set('uid:1234'); ref.child('users').child(auth.uid).child('opponent').set('uid:2345'); 

第二个set()操作将会失败,因为opponent属性在那个点上已经有了一个值。

你可以扩展它来validation对手必须相互引用:

 "users": { "$uid": { "opponent: { ".write": "!data.exists()" ".validate": "newData.parent().parent().child(newData.val()) .child('opponent').val() == $uid" } } } 
  1. 从正在写入的opponent ,我们回到users两个级别: newData.parent().parent()
  2. 然后我们进入对手的节点: child(newData.val())
  3. 然后我们validation对手的opponent属性是否与我们的uid: child('opponent').val() == $uid匹配。

现在上面的两个写操作都会失败,因为他们只是一次一个地设置对手。 要解决这个问题,你需要执行一个所谓的多位置更新 :

 var updates = {}; updates['users/'+auth.uid+'/opponent'] = 'uid:1234'; updates['users/uid:1234/opponent'] = auth.uid; ref.update(updates); 

我们现在将一个update()命令发送给Firebase服务器,将uid写入两个对手。 这将满足安全规则。

一些注意事项:

  • 这些只是让你开始的一些例子。 虽然他们应该工作,你需要编写自己的规则,以满足您的安全需求。
  • 这些规则只是处理对手的写作。 您可能还想testing游戏结束时发生的情况,并且需要清除对手。

您也可以查看事务操作 。

Firebase事务处理确保您正在处理的当前数据集实际上是数据库中的数据,确保您正在更新处于正确状态的数据。 该文件表明,这是避免竞争条件,如你所描述的build议的方式。

这样的东西(在IOS中,并警告 – 未经testing):

 NSString* user1Key = @"-JRHTHaIs-jNPLXOQivY"; NSString* user2Key = @"-NFHUaIs-kNPLJDHuvY"; Firebase *user1Ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com.users/-JRHTHaIs-jNPLXOQivY/opponent"]; Firebase *user2Ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com.users/-NFHUaIs-kNPLJDHuvY/opponent"]; //See if the proposed opponent does not yet have a match [user2Ref runTransactionBlock:^FTransactionResult *(FMutableData *opponent) { if (opponent.value == [NSNull null]) { //They have no match - update with our key and signal success [opponent setValue:user1Key]; return [FTransactionResult successWithValue: opponent]; } else { return [FTransactionResult abort]; //They already have an opponent - fail //Notify the user that the match didn't happen } } andCompletionBlock:^(NSError *error, BOOL committed, FDataSnapshot *snapshot) { if (!error && committed) { //The transaction above was committed with no error //Update our record with the other player - we're matched! [user1ref setValue:user2Key]; //Do whatever notification you want } else { //Notify that the matchup failed } }]; 
Interesting Posts