4

This is how the DB presented over Firebase

Parent
      Match
           User 1
                 Opponent : User 2
                 State    : "NotReady"
           User 2 
                 Opponent : User 1
                 State    : "NotReady"

I'm trying to update the values of State (each user individually) with RunTransaction.

What I was trying to do :

  • Check that the item haven't removed(is not nil/null)
  • If he is exist - update the value
  • if he doesnt exists - manipulate UI
  • if he suddenly deleted - abort the transaction (Possible condition - if two close event running at the same time, one updating "State" and other deletes the all key(removeValue)

     let path =  "Parent/Match/User 1/state"
    
     let futureRef = Firebase(url: path)
             futureRef.runTransactionBlock({
        (currentData:FMutableData!) in
        let value = currentData.value as? String
    
        if value != nil  {
            currentData.value = "Ready"
    
            return FTransactionResult.successWithValue(currentData)
        }
    
            return FTransactionResult.abort()
    
        }, andCompletionBlock: {
            // Completion Check
            (error:NSError!, success:Bool, data:FDataSnapshot!) in
    
            if error == nil && success && data.value as! String == "Ready"
            {
                //Value is not null(not removed) and he is ready
                  ManipulateUI()
    
            }
            else 
          {
    
            //Value deleted
           }
    
    
            }
        )
    

But for some reason - I'm getting currentData that is nil and going straight to the Abort. Any suggestions? Thanks!!!

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Roi Mulia
  • 5,626
  • 11
  • 54
  • 105

1 Answers1

6

Firebase uses a compare-and-set approach to transactions, which I'll describe below. It explains why you're getting nil, but unfortunately means your current approach won't work.

When you invoke runTransactionBlock, the Firebase client will immediately invoke your block with its best guess for the current value of the location. This best guess may be correct, but will be nil if it doesn't know a current value for the location.

The Firebase client then sends the current value it gave your block and the value that you returned to the server. The Firebase server checks if the current value in the database is the same as the client's best guess. If so, it writes the new value you specified. If the value in the database is different, the server rejects the new value and send the rejection and the actual current value from the database to the client.

Your block is invoked again, but this time with the new "best guess" as to the current value.

This continues until either the server accepts the operation or until there have been too many tries (something 10 or 25 I think).

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thank you for the well explained answer. So if we break it down, at the current scenario im dealing with. Two close events - One update specific value and one removes it. What is the correct approach i should take? If you remember from the question i posted yesterday,with the timers(one deletes while one removes , so the end result is that the "late" user thinks he is "okay" to play while the values eventually removed)? Im stuck in this for the last couple days :/ – Roi Mulia Jan 15 '16 at 04:17
  • If you want to use a transaction, run it at a higher level so that you get the current value for both players at the same time. Alternatively: use security rules to validate the state transition of each player, so that there is no race condition. Something like this, might be a good read: https://www.firebase.com/blog/2014-02-04-firesafe-complex-security-logic-for-firebase.html – Frank van Puffelen Jan 15 '16 at 04:42
  • I guess something is kind of broken with my architecture, can you please expand on "run it on higher level"? + as far as i understood validation and rules can only "accept or decline" requests, but not manipulating them, so if possible m, what exactly did you meant by the "alternative". I hope not asking too many questions, i promise you - the final result of this app going to be amazing! :) – Roi Mulia Jan 15 '16 at 04:46
  • The article I linked validates state transitions, which means that it effectively controls the type of transitions the code can do. So it cannot determine what state transitions the code tries, but it can ensure it only allows valid transitions. – Frank van Puffelen Jan 15 '16 at 05:31
  • Thank you Frank. I'll read the article and will let you know if i came to conclusion ..! – Roi Mulia Jan 15 '16 at 10:36
  • Hey Frank. I have read the whole article, it's pretty high level - but it did gave me a lot of details i needed to know. Yet, i still haven't figured it out yet. Could you please explain me a single relation - If i send removeValue command, and Update command to the same node. What is the order? if there is one? one of them with higher priority than the other? – Roi Mulia Jan 15 '16 at 11:38
  • If they come from the same client, they will be executed in the order they are sent. Between multiple clients there are no ordering guarantees. So if you need cross-client ordering, you should typically use security rules (and potentially transactions) to build a state machine. – Frank van Puffelen Jan 15 '16 at 14:19
  • Alright, I'll give it a shot, thanks Frank! Cheers buddy – Roi Mulia Jan 15 '16 at 15:21