firebase best practice to prevent double spending in firestore. (Lock a document or field while modifying it)

Let’s say I have a collection of Employees and a collection of Departments. And I have cloud functions set to update the employees count in a department whenever an employee is created or deleted.

import * as functions from "firebase-functions";
import admin = require("firebase-admin");


export const handleEmployeeDeletion = functions.firestore
  .document('Employee/{employeeId}')
  .onDelete((snapshot, context) => {
    const departmentRef = admin.firestore()
      .collection("Department").doc(snapshot.data.departmentId);
    return departmentRef.update({
      employeesCount: admin.firestore.FieldValue.increment(-1)
    })
  })

What happens if two employee documents are deleted exactly at the same time? Am I safe from double-spending as long as I’m using the firestore.FieldValue.increment rather than reading the current value to the RAM of the Cloud Function and then writing the updated (And possibly outdated) value from there?

How about if the change that needs to be done to the field involves more calculations than simply incrementing? How can I make the changes while making sure there’s no chance multiple sessions will be modifying the same field at the same time after reading the same old value of the field?

>Solution :

What happens if two employee documents are deleted exactly at the same time?

The function is invoked twice, meaning that the value is decremented twice. This also means that you’ll have to pay for two writes.

Am I safe from double-spending as long as I’m using the firestore.FieldValue.increment rather than reading the current value to the RAM of the Cloud Function and then writing the updated (And possibly outdated) value from there?

If you’re using the incrementation, you save two reads. Because if you want to read them first and then increment, you have to pay two reads and two writes.

How about if the change that needs to be done to the field involves more calculations than simply incrementing? How can I make the changes while making sure there’s no chance multiple sessions will be modifying the same field at the same time after reading the same old value of the field?

In that case, you need to use a transaction, which assures you that you’ll always have consistent data, no matter how many concurrent increment operations are performed.

Leave a Reply