I am trying to create a loop to execute transactions over more than 500 documents.
I have had a close look at posts like this one, however, the solutions there only apply to batch writes.
In my case I really need to perform an actual transaction where I lock down a doc I am reading from.
I am testing the code from the functions shell.
I am not really clear on what I should be doing differently since I am actually awaiting the read before performing the writes, and returning over each iteration?
Any hint would be greatly appreciated. Thanks.
My code:
import * as fb from "../utils/init";
import * as functions from "firebase-functions";
import { sub, add, startOfDay } from "date-fns";
import { getFirestore, FieldValue } from "firebase-admin/firestore";
import { Transaction } from "@app/types";
const chunk = require("lodash.chunk");
const db = getFirestore();
export const cronExpireCredits = functions
  .runWith({
    failurePolicy: true,
  })
  .region(...fb.REGIONS)
  .pubsub.schedule("0 1 * * *")
  .timeZone("Europe/London")
  .onRun(async (context) => {
    const startDate = sub(startOfDay(new Date()), {
            months: Number(fb.EXPIRY_WINDOW_IN_MONTHS),
          });
    const endDate = add(startDate, { days: 1 });
    try {
      const querySnapshot = await db
        .collectionGroup("credits")
        .where("expire_on", ">", startDate)
        .where("expire_on", "<", endDate)
        .get();
      if (querySnapshot.empty) {
        console.log("No matching documents.");
        return;
      }
      chunk(querySnapshot.docs, 500).map(async (userDocs: any) => {
        return await db.runTransaction(async (transaction) => {
          userDocs.forEach(async (userDoc: any) => {
            let userPublicDoc = await transaction.get(
              db
                .collection("users")
                .doc(userDoc.data().assigned_to)
                .collection("public")
                .doc("profile")
            );
            let userCurrentBalance = userPublicDoc.data()?.balance;
            let creditsAmountToExpire = userDoc.data().balance;
            let newBalance = userCurrentBalance - creditsAmountToExpire;
            transaction.update(userPublicDoc.ref, {
              balance: newBalance,
            });
            transaction.update(userDoc.ref, {
              status: "expired",
              balance: 0,
            });
            const balanceTransaction: Transaction = { // my transaction object };
            transaction.set(
              userPublicDoc.ref
                .collection("balance")
                .doc(`bal-${context.eventId}`),
              balanceTransaction
            );
            const transaction: Transaction = { // my transaction object };
            transaction.set(
              userDoc.ref
                .collection("transactions")
                .doc(`cpt-${context.eventId}`),
              transaction
            );
          });
        });
      });
    } catch (e) {
      console.log("error", e);
    }
  });
 
     
    