MongoDB transactions.

Basics

with client.start_session() as s:
    with s.start_transaction():
        db.accounts.update_one({"_id": a}, {"$inc": {"bal": -100}}, session=s)
        db.accounts.update_one({"_id": b}, {"$inc": {"bal": 100}}, session=s)

Requires replica set or sharded cluster.

with_transaction (auto-retry)

def txn(session):
    db.accounts.update_one({"_id": a}, {"$inc": {"bal": -100}}, session=session)
    db.accounts.update_one({"_id": b}, {"$inc": {"bal": 100}}, session=session)

with client.start_session() as s:
    s.with_transaction(txn)

Retries on transient errors.

Node

const session = client.startSession();
try {
    await session.withTransaction(async () => {
        await accounts.updateOne({ _id: a }, { $inc: { bal: -100 } }, { session });
        await accounts.updateOne({ _id: b }, { $inc: { bal: 100 } }, { session });
    });
} finally {
    await session.endSession();
}

Limits

  • 1MB oplog entry per op (60s txn limit by default).
  • No CRUD on system collections.
  • Some ops restricted.

Performance cost

Transactions slower than single-doc updates. Use only when truly needed.

Alternative: schema design

Often you can put related data in single doc + atomic updates:

db.orders.findOneAndUpdate(
    { _id: orderId, "items.id": itemId },
    { $inc: { "items.$.qty": 1, total: 10 } }
)

Single-doc atomic. No transaction needed.

Read concern

s.start_transaction(read_concern=ReadConcern("snapshot"))

snapshot is default for txns. Consistent point-in-time read.

Write concern

s.start_transaction(write_concern=WriteConcern("majority"))

majority for durability.

Error handling

from pymongo.errors import OperationFailure

try:
    s.with_transaction(txn)
except OperationFailure as e:
    if e.has_error_label("UnknownTransactionCommitResult"):
        # ambiguous; idempotent retry safe
        pass

Two-phase commit (manual)

For cross-database / cross-cluster: use two-phase commit pattern manually. Mongo’s transactions cover one cluster.

Common mistakes

  • Long-running txns (>60s default) → abort.
  • Txns to avoid schema design issue (denormalize instead).
  • Forgetting session= param on each op.
  • Txns in non-replica-set Mongo.
  • Heavy writes in txn → contention.

Read this next

If you want my txn patterns, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .