Transactions: Patterns and Pitfalls
11 min read
Working Safely with Multi-Document Transactions
MongoDB transactions are ACID-compliant, but they come with trade-offs: they acquire collection-level locks, add round-trip latency, and automatically abort after 60 seconds by default. Use them only when atomicity across documents genuinely matters.
With Mongoose
js
import mongoose from "mongoose";
async function transferCredits(fromId, toId, amount) {
const session = await mongoose.startSession();
session.startTransaction();
try {
await Wallet.findByIdAndUpdate(
fromId,
{ $inc: { credits: -amount } },
{ session, new: true, runValidators: true }
);
await Wallet.findByIdAndUpdate(
toId,
{ $inc: { credits: amount } },
{ session, new: true }
);
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
throw err;
} finally {
session.endSession();
}
}Retryable Writes vs Transactions
For a single-document operation, always prefer the built-in atomicity — no session needed. Use retryable writes (retryWrites=true in the connection string) to handle transient network errors without manual retry loops.
- Single-document atomic: no transaction needed
- Two or more collections that must stay consistent: use a transaction
- Network hiccup on insert/update: retryable writes handle it automatically
- Long-running operation (>60 s): break it into smaller batches instead
Transactions require a replica set (or sharded cluster). A standalone mongod instance does not support multi-document transactions. MongoDB Atlas always runs as a replica set, so you are covered there.
Ready to test yourself?
Practice MongoDB— quiz & coding exercises