FireBase - 维护/保证数据一致性

时间:2016-01-08 13:19:18

标签: ios swift firebase firebase-security consistency

我试图了解以下情况的正确方法:

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

让我们假设5个用户"日志"同时进入我的应用程序,每个人都在"搜索"比赛。每个用户都拥有一个名为opponent的属性,该属性等于对手uniqueID(初始值等于""。到目前为止一直很好。

假设用户1与用户3匹配。用户1会将自己的oppoent值更新为用户3 uniqueID ,并对用户3执行相同操作

问题

1)如果在同一时刻,用户2试图与用户3相同怎么办? 2)如果在同一时刻,用户3试图对用户4这样做怎么办?

要点

是否可以锁定"用户值?或者一旦改变就冻结它们?我的方法是错误的吗?

我在考虑使用Security RulesValidation来创建一致性,但我可能选择了错误的技术(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"
      }
      }

    }
}

2 个答案:

答案 0 :(得分:4)

您可以使用Firebase security rules验证很多内容。

例如,你可以说只有当前没有对手的对手才能写出对手:

"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属性此时已有值。

你可以扩展它以验证对手必须互相引用:

"users": {
  "$uid": {
    "opponent: {
      ".write": "!data.exists()"
      ".validate": "newData.parent().parent().child(newData.val())
                    .child('opponent').val() == $uid"
    }
  }
}
  1. 从正在撰写的opponent开始,我们将两个级别上升回usersnewData.parent().parent()
  2. 然后我们进入对手的节点:child(newData.val())
  3. 然后我们验证对手的opponent属性是否与我们的uid匹配:child('opponent').val() == $uid
  4. 现在来自上面的两个写操作都会失败,因为它们一次只设置一个对手。要解决此问题,您需要执行所谓的multi-location update

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

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

    一些注意事项:

    • 这些只是让你入门的一些例子。虽然它们应该可以工作,但您需要编写自己的规则以满足您的安全需求。
    • 这些规则只是处理对手的写作。您可能还想测试游戏结束时会发生什么,并且您需要清除对手。

答案 1 :(得分:2)

您还可以查看transaction operation

Firebase事务确保您正在执行的当前数据集实际上是数据库中的数据,从而保证您正在更新处于正确状态的数据。文档表明,这是避免竞争条件的推荐方法,如您所描述的。

像这样(在IOS中,警告 - 未经测试):

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 
    }

}];