MongoDB Transactions

A transaction is a group of database operations that must all succeed together or all fail together. If any single operation in the group fails, every other operation in that group is rolled back — meaning the database returns to the state it was in before any of those operations ran.

Transactions follow the ACID properties, which guarantee data consistency and reliability even when errors occur or multiple users access the database at the same time.

Understanding ACID Properties

Atomicity

All operations in a transaction are treated as a single unit. Either all of them succeed, or none of them are applied. There is no partial success.

Consistency

The database always moves from one valid state to another. Rules and constraints defined in the schema are always satisfied before and after a transaction.

Isolation

Transactions running at the same time do not interfere with each other. Each transaction sees data as if it is the only one running, even when multiple users act simultaneously.

Durability

Once a transaction is committed (finalized), the changes are permanently saved. Even if the server crashes immediately after, the data is not lost.

Why Transactions Are Needed

Consider a bank transfer. Money moves from Account A to Account B. This involves two operations:

  1. Deduct ₹5,000 from Account A
  2. Add ₹5,000 to Account B

If the server crashes or an error occurs between these two steps, Account A loses ₹5,000 but Account B never receives it. A transaction wraps both operations — if either fails, both are reversed, and no money is lost.

When to Use Transactions in MongoDB

  • Financial operations involving debit and credit
  • Order placement that updates inventory and creates an order record simultaneously
  • User registration that writes to multiple collections at once
  • Any scenario where two or more writes must succeed or fail together

Single Document vs Multi-Document Transactions

Single Document (Atomic by Default)

MongoDB guarantees atomicity for operations on a single document without needing an explicit transaction. An update that modifies multiple fields in one document is always atomic — either all fields update or none do.

db.accounts.updateOne(
  { accountId: "A001" },
  { $set: { balance: 15000, lastUpdated: ISODate("2025-01-01") } }
)

Both balance and lastUpdated update atomically — no transaction keyword needed.

Multi-Document Transactions

When multiple documents or collections must be updated together atomically, an explicit transaction is required. MongoDB supports multi-document transactions starting from version 4.0, and they require a Replica Set or Sharded Cluster (not supported on standalone servers).

Using Transactions in MongoDB Shell

Step 1: Set Up the Collections

use bankDB

db.accounts.insertMany([
  { accountId: "A001", holder: "Pradeep Kumar", balance: 20000 },
  { accountId: "A002", holder: "Rekha Sharma", balance: 5000 }
])

Step 2: Start a Session

Transactions in MongoDB run inside a session. A session is a container that groups related operations together.

const session = db.getMongo().startSession()

Step 3: Start the Transaction

session.startTransaction()

Step 4: Perform Operations Inside the Transaction

const accounts = session.getDatabase("bankDB").accounts

// Deduct from Account A001
accounts.updateOne(
  { accountId: "A001" },
  { $inc: { balance: -5000 } },
  { session }
)

// Add to Account A002
accounts.updateOne(
  { accountId: "A002" },
  { $inc: { balance: 5000 } },
  { session }
)

Both operations run inside the same session and are part of the same transaction.

Step 5: Commit or Abort

If both operations succeed:

session.commitTransaction()
session.endSession()

If an error occurs and the changes must be reversed:

session.abortTransaction()
session.endSession()

Transactions Using try-catch (Recommended Pattern)

In real applications, a try-catch block handles errors automatically.

const session = db.getMongo().startSession()

try {
  session.startTransaction()

  const accounts = session.getDatabase("bankDB").accounts

  accounts.updateOne(
    { accountId: "A001" },
    { $inc: { balance: -5000 } },
    { session }
  )

  accounts.updateOne(
    { accountId: "A002" },
    { $inc: { balance: 5000 } },
    { session }
  )

  session.commitTransaction()
  print("Transaction committed successfully.")

} catch (error) {
  session.abortTransaction()
  print("Transaction aborted. Error: " + error)

} finally {
  session.endSession()
}

If either update throws an error, the catch block runs abortTransaction(), undoing all changes. The finally block always ends the session cleanly, regardless of success or failure.

Transactions Across Multiple Collections

A single transaction can span multiple collections in the same database.

const session = db.getMongo().startSession()

try {
  session.startTransaction()

  const db = session.getDatabase("shopDB")

  // Create the order
  db.orders.insertOne(
    { orderId: 2001, customerId: "C101", product: "Laptop", qty: 1 },
    { session }
  )

  // Reduce inventory
  db.inventory.updateOne(
    { product: "Laptop" },
    { $inc: { stock: -1 } },
    { session }
  )

  session.commitTransaction()

} catch (error) {
  session.abortTransaction()
} finally {
  session.endSession()
}

If inventory runs out (stock goes negative) and an application-level check throws an error before commit, the order insert also rolls back — maintaining data consistency.

Transaction Limitations

Performance Overhead

Transactions add overhead — they lock resources and require coordination between nodes. Avoid using transactions for every operation. Use them only when atomicity across multiple documents is truly required.

Time Limit

By default, a transaction must complete within 60 seconds. Long-running transactions are automatically aborted. This limit can be adjusted but long transactions indicate a design issue.

Requires Replica Set

Multi-document transactions only work on a MongoDB Replica Set or Sharded Cluster. A standalone MongoDB instance does not support them. MongoDB Atlas clusters are always replica sets, so transactions work there by default.

Retry Logic for Transient Errors

Network issues or temporary resource contention can cause a transaction to fail with a transient (temporary) error. MongoDB recommends retrying the entire transaction when this happens.

function runTransactionWithRetry(txnFunc, session) {
  while (true) {
    try {
      txnFunc(session)
      break
    } catch (error) {
      if (error.hasErrorLabel("TransientTransactionError")) {
        // Retry the transaction
        continue
      }
      throw error
    }
  }
}

Transaction Key Concepts Summary

ConceptMeaning
SessionA container for grouping operations together
startTransaction()Begins the transaction inside a session
commitTransaction()Saves all changes permanently
abortTransaction()Reverses all changes in the transaction
endSession()Closes the session after commit or abort
ACIDAtomicity, Consistency, Isolation, Durability

Summary

MongoDB transactions ensure that groups of operations either fully succeed or fully fail, preventing data inconsistency. ACID properties — Atomicity, Consistency, Isolation, and Durability — form the foundation of transaction guarantees. Single-document operations are atomic by default without needing explicit transactions. Multi-document transactions require a session, a start, and either a commit or abort. They work only on Replica Sets or Sharded Clusters. Using try-catch blocks with transactions ensures errors trigger automatic rollback. Transactions are powerful but should be reserved for operations where atomicity across multiple documents is genuinely necessary.

Leave a Comment