ejemplos ACID

https://scotch.io/tutorials/java-and-mongodb-40-support-for-multi-document-acid-transactions579

MongoDB 4.0 with multi-document ACID transactions

Multi-document ACID transactions in MongoDB are very similar to what you probably already know from traditional relational databases.

MongoDB’s transactions are a conversational set of related operations that must atomically commit or fully rollback with all-or-nothing execution.

Transactions are used to make sure operations are atomic even across multiple collections or databases. Thus, with snapshot isolation reads, another user can only see all the operations or none of them.

Let’s now add a shopping cart to our example.

For this example, 2 collections are required because we are dealing with 2 different business entities: the stock management and the shopping cart each client can create during shopping. The lifecycle of each document in these collections is 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. I created a POJO to represent it : Product.java.

{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(5) }

A shopping cart is created when a client adds its first item in the cart and is removed when the client proceeds to checkout or leaves the website. I created a POJO to represent it : Cart.java.

{
    "_id" : "Alice",
    "items" : [
        {
            "price" : NumberDecimal("3"),
            "productId" : "beer",
            "quantity" : NumberInt(2)
        }
    ]
}

The challenge here resides in the fact that I cannot sell more than I possess: if I have 5 beers to sell, I cannot have more than 5 beers distributed across the different client carts.

To ensure that, I have to make sure that the operation creating or updating the client cart is atomic with the stock update. That’s where the multi-document transaction comes into play. The transaction must fail in the case someone tries to buy something I do not have in my stock. I will add a constraint on the product stock:

Node that this is already included in the Java code.

To monitor our example, we are going to use MongoDB Change Streams that were introduced in MongoDB 3.6.

In each of the threads of this process called ChangeStreams.java, I am going to monitor one of the 2 collections and print each operation with its associated cluster time.

In this example we have 5 beers to sell. Alice wants to buy 2 beers but we are not going to use the new MongoDB 4.0 multi-document transactions for this. We will observe in the change streams two operations : one creating the cart and one updating the stock at 2 different cluster times.

Then Alice adds 2 more beers in her cart and we are going to use a transaction this time. The result in the change stream will be 2 operations happening at the same cluster time.

Finally, she will try to order 2 extra beers but the jsonSchema validator will fail the product update and result in a rollback. We will not see anything in the change stream. Here is the Transaction.java source code:

Here is the console of the Change Stream :

$ ./change-streams.sh

Watching test.cart

Watching test.product

Timestamp{value=6570052721557110786, seconds=1529709604, inc=2} => Cart{id='Alice', items=[Item{productId=beer, quantity=2, price=3}]}

Timestamp{value=6570052734442012673, seconds=1529709607, inc=1} => Product{id='beer', stock=3, price=3}

Timestamp{value=6570052764506783745, seconds=1529709614, inc=1} => Product{id='beer', stock=1, price=3}

Timestamp{value=6570052764506783745, seconds=1529709614, inc=1} => Cart{id='Alice', items=[Item{productId=beer, quantity=4, price=3}]}

As you can see here, we only get four operations because the two last operations were never committed to the database, and therefore the change stream has nothing to show.

You can also note that the two first cluster times are different because we did not use a transaction for the two first operations, and the two last operations share the same cluster time because we used the new MongoDB 4.0 multi-document transaction system, and thus they are atomic.

Here is the console of the Transaction java process that sum up everything I said earlier.

$ ./transactions.sh

Database state:

Product{id='beer', stock=5, price=3}

No carts...

######### NO TRANSACTION #########

Alice wants 2 beers.

We have to create a cart in the 'cart' collection and update the stock in the 'product' collection.

The 2 actions are correlated but can not be executed on the same cluster time.

Any error blocking one operation could result in stock error or a sale of beer that we can’t fulfill as we have no stock.

Alice adds 2 beers in her cart.

Sleeping 3 seconds...

Trying to update beer stock : -2 beers.

####################################

Database state:

Product{id='beer', stock=3, price=3}

Cart{id='Alice', items=[Item{productId=beer, quantity=2, price=3}]}

Sleeping 3 seconds...

######### WITH TRANSACTION #########

Alice wants 2 extra beers.

Now we can update the 2 collections simultaneously.

The 2 operations only happen when the transaction is committed.

Updating Alice cart : adding 2 beers.

Sleeping 3 seconds...

Trying to update beer stock : -2 beers.

####################################

Database state:

Product{id='beer', stock=1, price=3}

Cart{id='Alice', items=[Item{productId=beer, quantity=4, price=3}]}

Sleeping 3 seconds...

######### WITH TRANSACTION #########

Alice wants 2 extra beers.

This time we do not have enough beers in stock so the transaction will rollback.

Updating Alice cart : adding 2 beers.

Sleeping 3 seconds...

Trying to update beer stock : -2 beers.

##### MongoCommandException #####

##### STOCK CANNOT BE NEGATIVE #####

####### ROLLBACK TRANSACTION #######

####################################

Database state:

Product{id='beer', stock=1, price=3}

Cart{id='Alice', items=[Item{productId=beer, quantity=4, price=3}]}

Last updated

Was this helpful?