We have a booking app that uses Vuejs and Cloud Firestore (client library web version 9).
Scenario: Two customers come to our booking site at the same time and try to book the last available package at the same time. To save the reservation, we are using firestore transactions. However, if we try to book the last package from two devices and hit the submit button "simultaneously", the code below allows both orders to be accepted. Also, the package "sold" field is only incremented. Is there any way we can improve the following code to prevent overbooking?
const packageDocRef = doc(db, ...);
try {
const orderId = await runTransaction(db, async (transaction) => {
const packageDoc = await transaction.get(packageDocRef);
if (!packageDoc.exists()) {
throw "Package does not exist!";
}
const quantity = packageDoc.data().quantity;
const sold = parseInt(packageDoc.data().sold + 1);
if (quantity >= sold) {
transaction.update(packageDocRef, { sold: increment(1) });
const orderRef = await addDoc(collection(db, collections.ORDERS), reservation);
if(orderRef.id){
dispatch ("saveTravellerDetails", { orderId: orderRef.id });
return orderRef.id;
}else{
throw "Error saving reservation!";
}
} else {
return Promise.reject("Sorry! Not enough spots available.");
}
});
console.log("Reservation created ");
return { orderId: orderId };
} catch (e) {
// This will be a "Not enough spots available" error.
console.error(e);
return { error: e };
}
Ok, I saw the error in my ways. I'm posting here in the event it can help someone else.
Instead of await addDoc(collection(db, collections.ORDERS), reservation);
I needed to use transaction.set(orderDocRef, reservation);