MongoDB Transactions Interview Questions

Introduction

This guide covers essential MongoDB transaction concepts commonly asked in technical interviews. Each question includes detailed answers and practical examples.

Medium

1. What are MongoDB transactions and why are they important?

MongoDB transactions allow you to execute multiple operations as a single unit of work, ensuring data consistency across multiple documents and collections. Key benefits include:

  • Data Consistency
  • ACID Compliance
  • Error Handling
  • Rollback Capability
  • Isolation
ACID Properties:
  • Atomicity: All operations succeed or all fail
  • Consistency: Data remains in a valid state
  • Isolation: Concurrent transactions don't interfere
  • Durability: Committed changes are permanent
Hard

2. How do you implement transactions in MongoDB?

Transaction implementation in MongoDB:

1. Basic Transaction
// Start a session
const session = client.startSession();

try {
    // Start transaction
    session.startTransaction();
    
    // Perform operations
    await db.users.insertOne(
        { name: "John", balance: 100 },
        { session }
    );
    
    await db.accounts.updateOne(
        { userId: "123" },
        { $inc: { balance: 100 } },
        { session }
    );
    
    // Commit transaction
    await session.commitTransaction();
} catch (error) {
    // Abort transaction on error
    await session.abortTransaction();
    throw error;
} finally {
    // End session
    session.endSession();
}
2. Advanced Transaction
// Transaction with options
const session = client.startSession({
    causalConsistency: true,
    readConcern: { level: "snapshot" },
    writeConcern: { w: "majority" }
});

try {
    session.startTransaction({
        readConcern: { level: "snapshot" },
        writeConcern: { w: "majority" }
    });
    
    // Complex operations
    await db.orders.insertOne({
        userId: "123",
        items: ["item1", "item2"],
        total: 200
    }, { session });
    
    await db.inventory.updateMany(
        { itemId: { $in: ["item1", "item2"] } },
        { $inc: { quantity: -1 } },
        { session }
    );
    
    await db.users.updateOne(
        { _id: "123" },
        { $inc: { orderCount: 1 } },
        { session }
    );
    
    await session.commitTransaction();
} catch (error) {
    await session.abortTransaction();
    throw error;
} finally {
    session.endSession();
}
Hard

3. How do you handle transaction errors and retries?

Transaction error handling and retry logic:

1. Error Handling
async function executeTransaction(retries = 3) {
    const session = client.startSession();
    
    for (let i = 0; i < retries; i++) {
        try {
            session.startTransaction();
            
            // Transaction operations
            await db.users.updateOne(
                { _id: "123" },
                { $inc: { balance: -100 } },
                { session }
            );
            
            await db.orders.insertOne({
                userId: "123",
                amount: 100
            }, { session });
            
            await session.commitTransaction();
            return true;
        } catch (error) {
            await session.abortTransaction();
            
            if (error.hasErrorLabel("TransientTransactionError")) {
                // Retry on transient errors
                continue;
            }
            
            if (error.hasErrorLabel("UnknownTransactionCommitResult")) {
                // Check commit result
                const result = await session.commitTransaction();
                if (result) return true;
                continue;
            }
            
            throw error;
        } finally {
            session.endSession();
        }
    }
    
    throw new Error("Transaction failed after retries");
}
2. Retry Logic
async function withRetry(operation, maxRetries = 3) {
    let lastError;
    
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await operation();
        } catch (error) {
            lastError = error;
            
            if (error.hasErrorLabel("TransientTransactionError")) {
                // Exponential backoff
                await new Promise(resolve => 
                    setTimeout(resolve, Math.pow(2, i) * 100)
                );
                continue;
            }
            
            throw error;
        }
    }
    
    throw lastError;
}

// Usage
await withRetry(async () => {
    const session = client.startSession();
    try {
        session.startTransaction();
        // Transaction operations
        await session.commitTransaction();
    } catch (error) {
        await session.abortTransaction();
        throw error;
    } finally {
        session.endSession();
    }
});
Hard

4. How do you optimize transaction performance?

Transaction performance optimization:

1. Performance Optimization
// Optimize transaction settings
const session = client.startSession({
    causalConsistency: false,  // Disable if not needed
    readConcern: { level: "local" },  // Use appropriate level
    writeConcern: { w: 1 }  // Adjust based on requirements
});

// Batch operations
async function batchTransaction(operations) {
    const session = client.startSession();
    try {
        session.startTransaction();
        
        // Execute operations in parallel
        await Promise.all(operations.map(op => 
            db[op.collection][op.method](
                op.query,
                op.update,
                { session }
            )
        ));
        
        await session.commitTransaction();
    } catch (error) {
        await session.abortTransaction();
        throw error;
    } finally {
        session.endSession();
    }
}
2. Monitoring and Tuning
// Monitor transaction performance
async function monitorTransactions() {
    const metrics = await db.adminCommand({
        serverStatus: 1
    });
    
    // Check transaction metrics
    const transactionMetrics = metrics.transactions;
    console.log({
        activeTransactions: transactionMetrics.currentActive,
        committedTransactions: transactionMetrics.committed,
        abortedTransactions: transactionMetrics.aborted,
        averageTransactionTime: transactionMetrics.averageTransactionTime
    });
    
    // Tune transaction settings
    if (transactionMetrics.averageTransactionTime > 1000) {
        // Optimize transaction settings
        await db.adminCommand({
            setParameter: 1,
            transactionLifetimeLimitSeconds: 60
        });
    }
}
Hard

5. What are the transaction best practices?

Follow these transaction best practices:

1. Design Best Practices
// Transaction wrapper
async function withTransaction(operation) {
    const session = client.startSession();
    try {
        session.startTransaction({
            readConcern: { level: "snapshot" },
            writeConcern: { w: "majority" }
        });
        
        const result = await operation(session);
        await session.commitTransaction();
        return result;
    } catch (error) {
        await session.abortTransaction();
        throw error;
    } finally {
        session.endSession();
    }
}

// Usage example
await withTransaction(async (session) => {
    // Transaction operations
    const user = await db.users.findOne(
        { _id: "123" },
        { session }
    );
    
    if (!user) {
        throw new Error("User not found");
    }
    
    await db.orders.insertOne({
        userId: user._id,
        amount: 100
    }, { session });
    
    return user;
});
2. Implementation Best Practices
// Transaction manager
class TransactionManager {
    constructor(client) {
        this.client = client;
    }
    
    async execute(operation, options = {}) {
        const session = this.client.startSession(options);
        try {
            session.startTransaction(options);
            const result = await operation(session);
            await session.commitTransaction();
            return result;
        } catch (error) {
            await session.abortTransaction();
            throw error;
        } finally {
            session.endSession();
        }
    }
    
    async executeWithRetry(operation, maxRetries = 3) {
        let lastError;
        for (let i = 0; i < maxRetries; i++) {
            try {
                return await this.execute(operation);
            } catch (error) {
                lastError = error;
                if (error.hasErrorLabel("TransientTransactionError")) {
                    await new Promise(resolve => 
                        setTimeout(resolve, Math.pow(2, i) * 100)
                    );
                    continue;
                }
                throw error;
            }
        }
        throw lastError;
    }
}

Next Steps

Continue your MongoDB interview preparation with: