Skip to main content

Transactions

MongoDB introduced multi-document ACID transactions in version 4.0, enhancing its capabilities to ensure data integrity across multiple documents, collections, and databases. This feature brought MongoDB closer to traditional relational databases, providing a robust mechanism for handling complex transactional data operations.

Key Concepts

  1. ACID Properties: MongoDB transactions ensure Atomicity, Consistency, Isolation, and Durability across multiple documents. This means that a group of operations either all succeed together or fail together without affecting the database's consistency.
  2. Snapshot Isolation: MongoDB uses snapshot isolation to maintain consistency. This means that all reads within a transaction will see a consistent snapshot of the database, and modifications made by a transaction are invisible to others until the transaction commits.
  3. Change Streams: MongoDB allows applications to access real-time data changes without the complexity and risk of tailing the oplog. This feature, introduced in MongoDB 3.6, enables applications to stream changes to documents in a database in real-time.

Always start the ChangeStreams class in the transactions package first because it creates the product collection with the required JSON Schema. See the related blog post.

Example Scenario: Implementing a Shopping Cart

Switch to session3 for this exercise:

git stash
git checkout session3-complete

Let’s consider a scenario where a shopping cart application needs to manage product inventory and customer carts atomically to prevent selling more items than available in stock.

Understanding the Code Structure

The project contains two main classes:

  • ChangeStreams.java: Monitors data changes in MongoDB.
  • Transactions.java: Handles the transaction logic.

For this example, two collections are required because we are dealing with two different business entities: the stock management and the shopping cart each client can create during shopping. The lifecycles of each document in these collections are different.

A document in the product collection represents an item I’m selling. This contains the current price of the product and the current stock, created in a POJO to represent it Product.java.

Running the Demo

You'll need two terminals for this:

Run the ChangeStreams Class:

mvn spring-boot:run -Dspring-boot.run.arguments="change-streams"

Run the Transactions Class in a Separate Terminal:

mvn spring-boot:run -Dspring-boot.run.arguments="transactions"

Step 4: Observing Behaviors

  • Without Transaction: Attempt to update stock and add items to the cart separately. This can potentially lead to inconsistencies if one operation fails.
  • With Transaction: Perform both operations atomically. If the stock is insufficient, the transaction will roll back, preventing any changes to the database and ensuring consistency.

Here is an example of performing operations within a transaction:

session.startTransaction(TransactionOptions.builder().writeConcern(WriteConcern.MAJORITY).build());
aliceWantsTwoExtraBeers(session);
sleep();
removingBeerFromStock(session);
session.commitTransaction();

Explanation:

  • session.startTransaction(...): Begins a new transaction with majority write concern, ensuring that the operations are acknowledged by the majority of the nodes.
  • aliceWantsTwoExtraBeers(session): Adds two extra beers to Alice's cart within the transaction.
  • sleep(): Pauses the execution momentarily, simulating a delay.
  • removingBeerFromStock(session): Decreases the beer stock within the transaction.
  • session.commitTransaction(): Commits the transaction, making all changes permanent only if all operations succeed.

Conclusion

MongoDB’s support for multi-document transactions allows developers to handle complex data changes with ease, ensuring data integrity and consistency across operations. This makes MongoDB suitable for applications requiring robust transactional support, similar to traditional relational databases.