When attempting firebase database transactions, they fail without throwing any errors.
Here is the relevant code snippet:
Code:
import * as admin from "firebase-admin";
const db = admin.firestore();
class Doc {
first = 0;
second = 0;
third = 0;
id = Math.random();
constructor(data: any) {
if (data) Object.assign(this, data);
this.first += 1;
this.second += 2;
this.third += 3;
console.log(this);
}
serialize() {
return {
first: this.first,
second: this.second,
third: this.third,
id: this.id,
};
}
}
export function runSingleTransaction() {
return db
.runTransaction(async (transaction) => {
const docSnapshot = await db
.collection("transaction-test")
.doc("base-test")
.get();
let doc = docSnapshot.data();
const writeDoc = new Doc(doc);
if (doc) transaction.update(docSnapshot.ref, writeDoc.serialize());
else transaction.set(docSnapshot.ref, writeDoc.serialize());
return writeDoc;
}, {})
.then((wd) => {
console.log("the transaction passed");
return wd;
})
.catch((err) => {
console.log("the transaction failed");
return err;
});
}
Index:
import * as functions from "firebase-functions";
import {runSingleTransaction } from "./entrypoints";
export const runTransaction = functions.https.onRequest(
async (request, response) => {
response.set("Access-Control-Allow-Origin", "*");
const i = await runSingleTransaction();
response.status(200).send(JSON.stringify({ res: i }));
}
);
Test:
it.only("should run db transactions in practice", async () => {
const ret = [];
for (let i = 0; i < 10; i++) {
const res = axios.get(
"http://localhost:5001/guestfeedback-2f0f8/us-central1/runTransaction"
);
ret.push(res);
}
await Promise.all(ret);
});
The output can be viewed here: https://i.sstatic.net/Lkzzd.jpg https://i.sstatic.net/bCH76.jpg
Expected Behavior: In Firestore, we expect to see an increment of 10, with the first property being 10, the second being 20, and the third being 30. Logically, we anticipate failure and retry logs multiple times due to concurrent writes, but all logs are passing smoothly.
Is this the optimal way to handle concurrency? If so, what am I overlooking? If not, how should this use case be properly handled?