This chapter dives into how MongoDB implements multi-document ACID transactions, enabling applications to maintain data consistency across multiple documents and collections. We’ll discuss concepts, implementations, practical use cases, and advanced techniques for optimizing transactions in MongoDB.
startSession
and startTransaction
, followed by a series of read/write operations and concludes with either commitTransaction
or abortTransaction
.
const session = client.startSession();
session.startTransaction();
try {
await db.collection("accounts").updateOne(
{ _id: "A" }, { $inc: { balance: -100 } }, { session }
);
await db.collection("accounts").updateOne(
{ _id: "B" }, { $inc: { balance: 100 } }, { session }
);
await session.commitTransaction();
console.log("Transaction committed successfully");
} catch (error) {
await session.abortTransaction();
console.error("Transaction aborted due to error:", error);
} finally {
session.endSession();
}
commitTransaction
and abortTransaction
commitTransaction
: Finalizes the transaction, making changes permanent.abortTransaction
: Rolls back the transaction, restoring the initial state of the data.{ writeConcern: { w: "majority" } }
to ensure durability.readConcern
levels. For transactions, snapshot
read concern is typically used to ensure the transaction operates on a consistent snapshot of the data.
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" }
});
const session = client.startSession();
try {
session.startTransaction();
await db.collection("users").updateOne({ userId: "A" }, { $set: { status: "active" } }, { session });
await db.collection("orders").updateOne({ orderId: "1" }, { $set: { status: "confirmed" } }, { session });
await session.commitTransaction();
console.log("Transaction in sharded cluster committed successfully");
} catch (error) {
await session.abortTransaction();
console.error("Transaction aborted:", error);
} finally {
session.endSession();
}
"TransientTransactionError"
to help identify transient errors, which can be automatically retried.
async function runTransactionWithRetry(session, transactionFunction) {
while (true) {
try {
await transactionFunction(session);
break;
} catch (error) {
if (error.hasErrorLabel("TransientTransactionError")) {
console.log("Retrying transaction...");
} else {
throw error;
}
}
}
}
mongostat
, mongotop
, or MongoDB Atlas to keep an eye on transaction metrics.
const session = client.startSession();
session.startTransaction();
try {
// Check if product is in stock
const product = await db.collection("inventory").findOne({ productId: "12345" }, { session });
if (product.stock > 0) {
await db.collection("inventory").updateOne({ productId: "12345" }, { $inc: { stock: -1 } }, { session });
await db.collection("orders").insertOne({ orderId: "1001", productId: "12345", status: "confirmed" }, { session });
await session.commitTransaction();
console.log("Order placed and stock updated successfully");
} else {
throw new Error("Out of stock");
}
} catch (error) {
await session.abortTransaction();
console.error("Transaction aborted:", error);
} finally {
session.endSession();
}
Multi-document transactions in MongoDB allow applications to handle complex workflows requiring atomic operations across multiple documents and collections. By implementing these transactions, you can ensure data consistency and integrity across a variety of use cases, from banking to e-commerce. Happy coding !❤️