Home > Enterprise >  Correct handling of counters in Cloud Firestore
Correct handling of counters in Cloud Firestore

Time:09-28

Desired outcome

My app stores the events and responses of my users in Firebase Cloud Firestore. A user can save a response with different types regarding his attendance (e.g. "yes", "no", "maybe", "on vacation").

A counter keeps track of the different response types and is stored in the event document. Whenever a response is added or updated, a Cloud Function (onWrite trigger) will increase or decrease the counter in the event document. When all events are listed, the total number of responses can be shown immediately using the data from the counter fields.

Data structure in Cloud Firestore:

[events] / {eventDoc} / [responses] / {responseDocs}

The eventDoc stores all of the event details (e.g. title, start, end, location).

When users reply with their personal attendance details (e.g. "yes", "no", ...), a new responseDoc is created in the responses collection with their uid. So each response is a separate document!

Problem

Because of the 1 write per second limit, I am concerned that there may be delays and especially errors in the counters if a large number of users update their responses at the same time. The counters make little sense if they do not display correct data.

Should a distributed counter be used in this case?

I'm not sure if a distributed counter is even necessary here because user responses are not stored in one eventDoc, but in separate documents in the sub-collection.

Question

Should I use a triggered Cloud Function as described above or should I implement the adjustment of the counter on the client?

A transaction on the client side could ensure that a response would only be created or updated if the corresponding counter value on the event document is also updated. However, this could just lead to frustrated users, if the execution fails. And what about idempotent Cloud Functions?

Any feedback on this is very much appreciated!

CodePudding user response:

Because of the 1 write per second limit, I am concerned that there may be delays and especially errors in the counters if a large number of users update their responses at the same time.

If you're storing all responses into a single document, and you're afraid of this 1 write per second limitation, then you should consider creating four different documents, rather than a single one. So instead of having:

[events] / {eventDoc} / [responses] / {responseDocs}

You can add under the "responses" collection:

[events] / {eventDoc} / [responses]

Four documents:

{yesResponse}, {noResponse}, {maybeResponse}, {onVacationResponse}

In this way, you'll reduce the number of writes per single document. Regarding billing, it doesn't matter if you write into a single document, or four documents, the number of writes will be the same.

Besides that, if you check the official documentation, it is said that:

Sustaining a write rate above once per second increases latency and causes contention errors. This is not a hard limit, and you can surpass the limit in short bursts.

So you should consider surpassing the limit in short bursts, as mentioned there.

If that doesn't solve your problem either, then you should consider using the Realtime Database, where you can write up to 1,000 writes/second per database.

Luckily, even in the Realtime Database, you can use atomically increment a counter as you do in Cloud Firestore.


Edit:

Should I use a triggered Cloud Function as described above or should I implement the adjustment of the counter on the client?

I would go ahead and use cloud functions. In this case, all logic of incrementation is hidden in a trusted environment, and besides that, it will be much faster.

However, this could just lead to frustrated users, if the execution fails.

Yes, that's a terrible user experience, that should be avoided as much as possible.

  • Related