Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firestore runTransaction() and offline work

I'm implementing post liking and commenting functionality in Firestore using transactions. I use transaction because I need to add new field in likes/comments subcollection and update counter on the post, and also add post id to the user liked/commented post collection.

I noticed that if I'm offline and I request my post like this everything is OK:

val postDocRef = FirebaseUtil.postsColRef.document(postId)

postDocRef.get().addOnSuccessListener { doc ->
    val post = doc.toObject(Post::class.java)
    Timber.e(post.toString())
}

But if I do the same in transaction exception is thrown:

val postDocRef = FirebaseUtil.postsColRef.document(postId)

FirebaseUtil.firestore.runTransaction(Transaction.Function<Void> { transaction ->
    val post = transaction.get(postDocRef).toObject(Post::class.java)
}

Exception is:

com.google.firebase.firestore.FirebaseFirestoreException: UNAVAILABLE

Why offline mode is not working in transaction? Is it possible to implement this functionality (adding entry in subcollection and updating fields in different objects) in offline?

What could be drawbacks in replacing transaction with continueWithTask() call chain?

like image 488
Vadims Savjolovs Avatar asked Oct 08 '17 18:10

Vadims Savjolovs


1 Answers

No, this isn't possible with transactions because they are inherently network dependent. When you use a transaction, you are telling Firestore that you can only perform database operations synchronously, one client after the other. Transactions would be useful for stuff like in-game currency transfers where you need to ensure you don't accidentally double up your writes and give a user too much or too little money.

If your like counter needs to have perfect precision, I would suggest using a subcollection where each document contains a reference to the user who liked a given post. Then, in a Cloud Function you can use a transaction to count the number of users who've like a post and ensure there are no miscounts. This has the added benefit of letting you know who liked a post which should be future proof if you decided to add more like related features. On the client side, you can "trick" it by writing to the counter even though you don't have permission to do so. I haven't tested this, but I'm pretty sure the write will succeed locally and then fail only once you're back online. That doesn't matter though because the Cloud Function will then sync the counter server side.

On the other hand, if you don't really care about having super precise like counts, what you're looking for is the WriteBatch class. This one is new in Firestore and really cool. I'm in the process of getting a post on Firestore I wrote published, but here's an excerpt:

Cloud Firestore also includes an awesome new way to batch writes with the WriteBatch class. It’s very similar to the SharedPreferences.Editor you’ll find on Android. You can add or update documents in the WriteBatch instance, but they won’t be visible to your app until you call WriteBatch#commit(). I’ve created the standard Kotlin improvement where the batch lifecycle is managed for you — feel free to copypasta.

inline fun firestoreBatch(transaction: WriteBatch.() -> Unit): Task<Void> = FirebaseFirestore.getInstance().batch().run {
    transaction()
    commit()
}
like image 189
SUPERCILEX Avatar answered Sep 17 '22 02:09

SUPERCILEX