Concept
Multi-Document ACID Transactions in MongoDB
Historically, MongoDB only guaranteed atomic writes at the single-document level. Since version 4.0, MongoDB supports multi-document ACID transactions across replica sets and shards.
Transactions are executed using sessions:
const session = db.getMongo().startSession();
session.startTransaction();
try {
db.accounts.updateOne({ _id: 1 }, { $inc: { balance: -100 } }, { session });
db.accounts.updateOne({ _id: 2 }, { $inc: { balance: 100 } }, { session });
session.commitTransaction();
} catch (error) {
session.abortTransaction();
} finally {
session.endSession();
}Transaction Anomalies: Dirty Reads
Without transactions or under low isolation configurations (like Read Uncommitted), concurrent modifications can leak invalid data. A dirty read occurs when transaction T2 reads changes made by T1 before T1 commits, and T1 subsequently aborts:
BEGIN; UPDATE accounts SET balance = 6000 WHERE id = 1;
// no operations this step
T1 performs an update. In READ UNCOMMITTED isolation level, transactions can read dirty (uncommitted) writes directly from database memory buffers.
MongoDB defaults to Read Committed isolation level for transactions, preventing dirty reads.
Designing Relationships
- Embedded (One-to-Few): Store sub-documents inside parent records. Atomic by default, requires no multi-document transactions.
- References (One-to-Many / Many-to-Many): Link documents using target
_idvalues. Requires transaction management when keeping balances, sync references, or compound mutations up to date.
Common Mistakes
1. Reaching for transactions for every relation
Writing multi-document transactions in MongoDB is expensive because it creates lock contentions on database pages. Always attempt to design your schemas around embedded documents first. Single-document updates are atomic and scale infinitely without locking.
2. Executing long-running transactions
MongoDB transactions abort automatically if they exceed default execution time limits (e.g. 60 seconds). Do not execute slow external API calls or user prompts inside a transaction session.
Best Practices
- Validate Session Injection: Make sure all database queries inside a transaction explicitly pass the
{ session }configuration object, or the writes will execute outside the transaction. - De-normalize for Read Speed: Nest sub-documents if they are frequently read together, keeping changes in one single atomic operation.
- Configure Retry Logic: Network glitches can cause transaction aborts; write wrapper helper utilities that retry transaction sessions automatically.
