liveBook · Manning

Distributed transaction

As explained in MongoDB 8.0 in Action, Third Edition

A distributed transaction is a transaction whose operations involve multiple database resources or partitions and require coordinated commit or rollback.

A distributed transaction coordinates operations across multiple documents, collections, databases, or shards so they commit or roll back together. MongoDB’s single-document atomicity often avoids this need when related data is embedded, but multidocument transactions support cases requiring cross-resource consistency. section 8.4

Use distributed transactions deliberately:

  • Prefer embedded documents and arrays when they keep related updates within one document. section 8.4 section 8.4.3
  • Use the Callback API, whose higher-level transaction method starts, commits, and retries transactions after transient errors. section 8.4.3
  • Keep transactions short, indexed, and within the 60-second default timeout and 1,000-document modification limit. section 8.5
  • Expect additional performance overhead across multiple shards, and configure suitable read and write concerns. section 8.5

Account transaction workflow

A Node.js application connects with MongoClient, starts a session, sets snapshot reads, majority writes, and primary reads, then runs account, customer, and transaction updates inside session.withTransaction. Replace the connection string, save the script as transaction.js, and run it with an account ID. section 8.4.3

Listing 8.2 Executing a multidocument transaction with Node.js and Callback API
const { MongoClient } = require('mongodb')

// Replace with your actual connection string
const uri = "your_mongodb_connection_string"

async function run(accountId) {
  const client = new MongoClient(uri)

  try {
    await client.connect()
    const session = client.startSession()

    const transactionOptions = {
      readConcern: { level: 'snapshot' },
      writeConcern: { w: 'majority' },
      readPreference: 'primary'
    }

    await session.withTransaction(async () => {
      const accounts = client.db('sample_analytics').collection('accounts')
      const customers = client.db('sample_analytics').collection('customers')
      const transactions = client.db('sample_analytics').collection('transactions')
      const currentDate = new Date()

      const account = await accounts.findOne(
        { account_id: parseInt(accountId) },
        { session }
      )
      if (!account) throw new Error('Account not found')

      const accountsUpdateResult = await accounts.updateOne(
        { account_id: parseInt(accountId) },
        {
          $set: { limit: 12000, last_transaction_date: currentDate },
          $inc: { transaction_count: 1 }
        },
        { session }
      )

      const customersUpdateResult = await customers.updateMany(
        { accounts: { $in: [parseInt(accountId)] } },
        {
          $inc: { transaction_count: 1 },
          $set: { last_transaction_date: currentDate }
        },
        { session }
      )

      const transactionsInsertResult = await transactions.insertOne(
        {
          account_id: parseInt(accountId),
          transaction_count: account.transaction_count + 1,
          bucket_start_date: currentDate,
          bucket_end_date: currentDate,
          transactions: [
            {
              date: currentDate,
              amount: 1500,
              transaction_code: 'buy',
              symbol: 'amzn',
              price: '125.00',
              total: '187500.00'
            }
          ]
        },
        { session }
      )

      console.log("Transaction committed.")
      console.log("Accounts updated:", accountsUpdateResult.modifiedCount)
      console.log("Customers updated:", customersUpdateResult.modifiedCount)
      console.log("New transaction inserted:", transactionsInsertResult.insertedId)

    }, transactionOptions)

    session.endSession()
  } catch (error) {
    console.error("Transaction aborted due to error:", error)
  } finally {
    await client.close()
  }
}

// Get accountId from command line arguments
const accountId = process.argv[2]
if (!accountId) {
  console.error("Please provide an account ID as an argument.")
  process.exit(1)
}

run(accountId).catch(console.dir)

Inspect the complete Node.js implementation, including session setup, transaction options, collection access, and callback structure. section 8.4.3

How MongoDB 8.0 in Action, Third Edition covers it

How deep it goes
Explains Callback API execution, retries, deployment limits, forbidden operations, and cross-shard overhead without detailing transaction internals.
Examples
Demonstrates account, customer, and transaction updates through Node.js, with parallel implementations for PyMongo and Ruby.
What it assumes
Assumes basic MongoDB collections and application-driver usage while teaching session-based transaction setup from connection through callback execution.
What you can do after
Apply it via MongoDB driver Callback APIs with coordinated commit, rollback, and transient-error retry handling.
How it connects
Connects distributed transactions with ACID properties, embedded documents, schema design, sharding, indexes, read and write concerns, and error handling.

Also explained in

Compare how 4 books explain distributed transaction →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of MongoDB 8.0 in Action, Third Edition. The section links point to the parts of the book it draws on.