CPCODELAB
MongoDB

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