How it works
The explanation, with examples you can run.
A transfer
const session = db.getMongo().startSession()const accounts = session.getDatabase("shop").accountssession.startTransaction({ readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } })accounts.updateOne({ _id: "ana" }, { $inc: { balance: -30 } })accounts.updateOne({ _id: "ben" }, { $inc: { balance: 30 } })// another client reads heresession.commitTransaction()outside, before commit: [{"_id":"ana","balance":100},{"_id":"ben","balance":20}]after commit: [{"_id":"ana","balance":70},{"_id":"ben","balance":50}]Readers outside the transaction see the old values and are not blocked. The commit's write concern applies to the whole transaction; operations inside it do not take their own.
Conflicts
Two transactions that write the same document cannot both proceed. The second writer does not wait for the first: it fails at once.
112 WriteConflict ["TransientTransactionError"]The label tells you the whole transaction can be retried from the start. In application code, use the driver's callback API, which retries on that label and retries the commit on UnknownTransactionCommitResult:
await session.withTransaction(async () => { await accounts.updateOne({ _id: "ana" }, { $inc: { balance: -30 } }, { session }) await accounts.updateOne({ _id: "ben" }, { $inc: { balance: 30 } }, { session })})Because the callback can run more than once, keep side effects such as sending email or calling another service out of it.
What it costs
- Time limit. A transaction running longer than
transactionLifetimeLimitSeconds(60 by default) is aborted. - Cache pressure. Open transactions keep old versions of data in the WiredTiger cache. Abort abandoned transactions instead of leaving them open.
- Sharded commits. A transaction that writes to several shards commits with a two-phase protocol coordinated by one shard, adding round trips. Transactions that stay on one shard are cheaper.
- Concurrency. MongoDB 9.0 caps the number of open transactions (
maxConcurrentMultiDocumentTransactions, 10,000 by default) and rejects new ones withTooManyOpenTransactionsabove it. - Oplog. A large transaction is written as several oplog entries; each must still fit the 16 MB document limit.
The manual is direct about it: in most cases a distributed transaction costs more than single-document writes and should not replace effective schema design. Use transactions for invariants that genuinely span documents (a transfer between two accounts, a uniqueness rule across collections, an order plus its outbox event), and embed the rest.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.