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);
}
});