On collision course with Cloud Firestore

In Cloud Firestore, you can only update a single document about once per second, which might be too low for some high-traffic applications.

Dennis Alund
oddbit
4 min readOct 25, 2017

--

This is the opening line for a concept called “distributed counters” in the Cloud Firestore documentation, which goes on to explain a fairly complicated random distribution of shard counters to reduce the probability of collisions upon document write. But “reduce the probability” are the words to pay attention to.

So, what’s the problem here? What happens if you ignore the warnings and decide to try your luck?

Let’s find out.

This example will use Firebase cloud functions to simulate aggressively simultaneous actors to our highly popular (and also fictive) movie review application.

Just do it

First we’ll make a naive example of just boldly going for it. No transactions, no regrets.

The cloud function is triggered on each new review, counting and aggregating the number of reviews to get an average review score. But it’s not hard to imagine what kind of problems that will arise as soon as the pace of writes are picking up.

Firebase cloud functions log

As expected, we can see that there are several simultaneous cloud function executions that all read an old state of the counter, hence creating inconsistency in the counter. We did indeed fail to count all reviews.

Created 20 reviews, only counted 15

Transactions

Perhaps we’re having better luck if we’re wrapping it in a transaction so that the operation will retry until we can get our write operation through?

Unfortunately, as you can see in the logs below, the operation is indeed retrying as we want and expect (see the highlighted #16). But the result is less satisfying as it pushes the Firestore to a congested state where an exception is thrown.

Dirty writes on transactions are retried, but ends up causing “too much contention”

Distributed counters

It’s starting to be clear that we can rule out any simple and magical solution for this. The distributed counter is starting to sound like a good idea to try out. The Google Cloud Datastore (as in Google App Engine) is the very same technology on which Firestore is build upon, and the shard counter concept has been around for quite a long time. Long before Firebase realtime database existed.

Using Firebase Realtime Database for counting and aggregating Firestore data

Cloud Firestore is sitting conveniently close to the Firebase Realtime Database, and the two are easily available to use, mix and match within an application. You can freely choose to store data in both places for your project, if that serves your needs.

So, why not use the Realtime database for one of its strengths: to manage fast data streams from distributed clients. Which is the one problem that arises when trying to aggregate and count data in the Firestore.

First we add a little helper cloud function that scaffolds a default state of aggregated counter and average review score. Please note that this is written to the realtime Database.

After that, we’ll modify the cloud function that was making a transaction on the Firestore, above. Change the transaction to run on the realtime database instead.

Once again, pay attention to that we’re running the transaction against the realtime database to handle the high volume of writes better. And as you can see in the log below, the RTDB handles it with grace.

Dirty writes on transactions are retried, and look at the absence of errors

The use of realtime database in this case is much easier to manage and setup than the shard counter. And we also don’t need to worry about having to “reduce the probability” of document write collisions. We simply leave that to the RTDB implementation, which is designed to handle exactly this kind of high pace concurrent writes.

Reader’s exercise

For further exercise, you might want to find a way to mirror the counters and aggregated values back to Firestore in a controlled way. I’d be happy to see any actual implementation that uses this method and also mirrors the values back to a Firestore document in a smart way.

This is the Cloud Firestore logo

--

--

Dennis Alund
oddbit
Editor for

Google Developer Expert for Firebase, nerd and passionate problem solver | Founder of Kumpul coworking space in Bali