MongoDB Transactions
35 minMongoDB supports multi-document transactions that ensure ACID (Atomicity, Consistency, Isolation, Durability) properties across multiple operations. Transactions enable you to perform multiple read and write operations as a single atomic unit, where all operations succeed or all fail. Transactions are essential for maintaining data consistency in applications requiring multi-document updates. Understanding transactions enables you to build reliable, consistent applications.
Transactions in MongoDB use sessions to group operations. You start a session, begin a transaction, perform operations within the transaction context, and then commit or abort. All operations within a transaction see a consistent snapshot of data, and changes are only visible to other operations after commit. Transactions support read and write operations on multiple documents across multiple collections within the same database.
Transaction options include read concern (what data the transaction reads), write concern (when writes are acknowledged), and max commit time (maximum time to commit). Read concern 'snapshot' ensures the transaction reads from a consistent point in time. Write concern 'majority' ensures writes are acknowledged by a majority of replica set members. Understanding these options enables you to configure transactions for your consistency and performance requirements.
Error handling in transactions is critical. If any operation within a transaction fails, you should abort the transaction to roll back all changes. Transactions have time limits and size limits, and long-running transactions can impact performance. Best practices include keeping transactions short, handling errors appropriately, and understanding transaction limitations. Understanding error handling ensures reliable transaction execution.
Transactions are available in replica sets (starting MongoDB 4.0) and sharded clusters (starting MongoDB 4.2). In replica sets, transactions work across all members. In sharded clusters, transactions can span multiple shards, enabling distributed transactions. Understanding transaction availability and limitations helps you design applications that use transactions appropriately.
Best practices for transactions include using them only when necessary (single-document operations are atomic by default), keeping transactions short to reduce lock contention, handling errors and timeouts appropriately, and understanding performance implications. Transactions add overhead, so they should be used when multi-document atomicity is required. Understanding when and how to use transactions enables you to balance consistency with performance.
Key Concepts
- MongoDB transactions ensure ACID properties across multiple operations.
- Transactions group operations into atomic units.
- Sessions are used to manage transaction context.
- Transactions support read and write operations across collections.
- Transactions are available in replica sets and sharded clusters.
Learning Objectives
Master
- Implementing multi-document transactions
- Understanding transaction options and configuration
- Handling transaction errors and rollbacks
- Using transactions in replica sets and sharded clusters
Develop
- Understanding ACID properties in NoSQL databases
- Designing consistent multi-document operations
- Balancing consistency with performance
Tips
- Start session: const session = db.getMongo().startSession().
- Begin transaction: session.startTransaction().
- Pass session to operations: db.collection.updateOne({}, {}, { session }).
- Always handle errors and abort on failure: session.abortTransaction().
Common Pitfalls
- Not handling errors, leaving transactions open and blocking operations.
- Creating long-running transactions, causing performance issues.
- Using transactions unnecessarily, adding overhead for single-document operations.
- Not understanding transaction limitations, causing unexpected failures.
Summary
- MongoDB transactions ensure ACID properties for multi-document operations.
- Transactions use sessions to group operations atomically.
- Proper error handling is essential for reliable transactions.
- Transactions should be used when multi-document atomicity is required.
Exercise
Implement transactions for multi-document operations.
// Start a session
const session = db.getMongo().startSession()
// Begin transaction
session.startTransaction()
try {
// Perform multiple operations
db.accounts.updateOne(
{ _id: ObjectId("account1_id") },
{ $inc: { balance: -100 } },
{ session }
)
db.accounts.updateOne(
{ _id: ObjectId("account2_id") },
{ $inc: { balance: 100 } },
{ session }
)
db.transactions.insertOne({
fromAccount: ObjectId("account1_id"),
toAccount: ObjectId("account2_id"),
amount: 100,
timestamp: new Date()
}, { session })
// Commit transaction
session.commitTransaction()
console.log("Transaction committed successfully")
} catch (error) {
// Abort transaction on error
session.abortTransaction()
console.log("Transaction aborted:", error.message)
} finally {
// End session
session.endSession()
}
// Transaction with timeout
const session = db.getMongo().startSession()
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" },
maxCommitTimeMS: 5000
})
// Nested transaction example
const session = db.getMongo().startSession()
session.startTransaction()
try {
// Create order
const orderResult = db.orders.insertOne({
customerId: ObjectId("customer_id"),
items: [
{ productId: ObjectId("product_id"), quantity: 2, price: 50 }
],
total: 100,
status: "pending"
}, { session })
// Update inventory
db.inventory.updateOne(
{ productId: ObjectId("product_id") },
{ $inc: { stock: -2 } },
{ session }
)
// Create payment record
db.payments.insertOne({
orderId: orderResult.insertedId,
amount: 100,
method: "credit_card",
status: "completed"
}, { session })
// Update order status
db.orders.updateOne(
{ _id: orderResult.insertedId },
{ $set: { status: "completed" } },
{ session }
)
session.commitTransaction()
} catch (error) {
session.abortTransaction()
throw error
} finally {
session.endSession()
}
Exercise Tips
- Always use try-catch-finally for transaction error handling.
- Set transaction timeouts: maxCommitTimeMS to prevent long-running transactions.
- Use read concern 'snapshot' for consistent reads: readConcern: { level: 'snapshot' }.
- Keep transactions short: long transactions can cause performance issues.