0

I had a situation where I mistakenly prevented my firebase rules from writing to a ref and when I ran the code below "The value wasn't able to update, try again" printed out over 20x. The way I'm doing it now the transaction keeps trying itself until it's successful but after what I just encountered if it's not successful the transaction will run forever.

I was wondering is there a built in way to run a max # of tries if the transaction fails?

func runTransaction(on userId: String) {

    self.followButton.isEnabled = false

    let followersPath = Database.database().reference()
                       .child("users")
                       .child(userId)
                       .child("followersCount")

    followersPath.runTransactionBlock({ (mutableData: MutableData) -> TransactionResult in

        var currentCount = mutableData.value as? Int ?? 0
        mutableData.value = currentCount + 1
        return TransactionResult.success(withValue: mutableData)

    }, andCompletionBlock: { (error, completion, snap) in

       if !completion || (error != nil) {

           print("The value wasn't able to update, try again")

           self.runTransaction(on: userId)
           return
       }

       print("The value updated")

       let totalFollowersCount = snap?.value as? Int ?? 0

       self.myCellWithTheUserId.followersCount =  totalFollowersCount
       
       self.followButton.isEnabled = true
    })
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • What's this for `self.runTransaction(on: postId)` when it fails? It will automatically attempt the transaction again until it times out. – Jay Mar 25 '21 at 17:52
  • That’s the thing, the way I have it setup there is no time out, it’ll keep trying. When I first noticed the issue, it kept going and didn’t stop because it would’ve never succeeded because of incorrect rules. I had to shut down XCode. Is there a built in time out function? – Lance Samaria Mar 25 '21 at 17:54
  • Are you saying I don’t need to add that line and it’ll keep trying on its own? I didn’t know that. I thought once it fails that’s it – Lance Samaria Mar 25 '21 at 17:57
  • 1
    Nope. It retries; see [Transactions](https://firebase.google.com/docs/database/ios/read-and-write#save_data_as_transactions) *If the transaction is rejected, the server returns the current value to the client, which runs the transaction again with the updated value. This repeats until the transaction is accepted or too many attempts have been made*. If you need to abort the transaction you can `return TransactionResult.abort()` within the closure. – Jay Mar 25 '21 at 18:22
  • Oh thanks man! I had no idea that it tries on my own. I have this same code in 5 diff projects. I have to make some serious updates. Can you add this as an answer so that it’ll help the next person? I have a quick question. I didn’t include this in the question but I have some code that runs if it is successful, it would go inside my else statement. If I get rid of the else statement, add my success code which will still run if it fails, do I have to worry about it running my function again or will it try on its own on the backend? Basically I don’t want the success code to run twice. – Lance Samaria Mar 25 '21 at 18:29
  • I think I would have to see that. If you remove the else statement then there would be no where to add the code to run if it's successful. – Jay Mar 25 '21 at 18:32
  • I updated the question. I changed the else statement to use `return` instead. But with what I just learned from you there really is no need for the `return`. Wether it fails or not I just add the value to the followersCount property. If there were 3 users before the current user pressed follow then afterwards there would be 4 users. But if it fails it would just say 3 users again. – Lance Samaria Mar 25 '21 at 18:41

1 Answers1

3

Yes, there is a maximum number of retries of about 25. If it is exceeded, you will see a maxretry error code.

You typically shouldn't have to retry the transaction manually; Firebase of course handles this portion if there is a concurrency issue.

For incrementing values, you should use increment rather than a transaction. Which will avoid this write contention.

Kato
  • 40,352
  • 6
  • 119
  • 149
  • When is best to use a `transaction` vs an `increment`? All the examples that I see on SO for followers count, views count, etc all use transactions – Lance Samaria Mar 25 '21 at 19:03
  • The addition of the increment is fairly recent (circa last year). Always use increment when it works for your use case. Use transaction when you need special logic to determine the amount to increment or special handling in the success/abort use cases on the server. – Kato Mar 25 '21 at 19:12
  • Ok, thanks for that. I actually never heard of it before. I have to look into it more. I gave you an extra vote – Lance Samaria Mar 25 '21 at 19:27