Monday, January 27, 2020

Creating ACID Transactions in Azure Cosmos DB with SDK


     One of the big advantages of a Relational Databases, is the ACID transactions. Usually, an entity gets divided to many tables in relational databases. For example, Let's say we need to data about Users. You might have Users table which has the common user information, then you might have UserDetails, UserOptions, UserLocation tables which might have more information about the User. When you need to create a new user, you might need to insert information to all those tables. If one of those inserts fails, you don't want to continue and abort the inserts. You want all or nothing. Thanks to ACID Transactions, you can do that easily in SQL Server.

     What about Azure Cosmos DB? It's a NoSQL database, probably you can't do ACID Transactions right? WRONG! Azure Cosmos DB has been supporting ACID transaction for some time now. We were able to create ACID transactions by using stored procedures of Cosmos DB. Last year (2019) Cosmos DB team introduced ACID transactions to Cosmos DB SDK. Now, we can create transactions by using C# just like writing transactions by using SQLClient class for SQL Server!

      To create an ACID transaction in Cosmos DB SDK, we need to use TransactionalBatch object. You need add all operations in transaction to TransactionalBatch object. All the operations attached to the TransactionalBatch object must share the same partition key. In the following example, I created three objects and attach them to TransactionalBatch object. To start the transaction, I ran the ExecuteAsync() function.  This function runs the transaction and returns the responses for each operation.


var cosmosClient = new CosmosClient(connectionString);
Container container = cosmosClient.GetContainer("Stackoverflow", "Posts");
StackOverflowPost post = new StackOverflowPost() {
          id = Guid.NewGuid().ToString(),
          PostId = 99999,
          Title = "Created by Acid Demo",
          OwnerDisplayName = "Hasan Savran"
};
StackOverflowPost post2 = new StackOverflowPost() {
          id = Guid.NewGuid().ToString(),
          PostId = 99999,
          Title = "Created by Acid Demo 2",
          OwnerDisplayName = "Hasan Savran"
};
StackOverflowPost post3 = new StackOverflowPost() {
         id = Guid.NewGuid().ToString(),
         PostId = 99999,
         Title = "Created by Acid Demo 3",
         OwnerDisplayName = "Hasan Savran"
};
TransactionalBatch batch = container.CreateTransactionalBatch(new PartitionKey(99999))
.CreateItem(post)
.CreateItem(post2)
.CreateItem(post3);
TransactionalBatchResponse batchResponse = await batch.ExecuteAsync();   


     If you like to see the response for each transaction. You can use TransactionalBatchOperationResult object. If any of the operation fails, you should be able to find with this way.

TransactionalBatchResponse batchResponse = await batch.ExecuteAsync();
using (batchResponse)
{
    if (batchResponse.IsSuccessStatusCode)
    {
      TransactionalBatchOperationResult post1t = batchResponse.GetOperationResultAtIndex(0);
      StackOverflowPost post1result = post1t.Resource;
      TransactionalBatchOperationResult post2t = batchResponse.GetOperationResultAtIndex(1);
      StackOverflowPost post2result = post2t.Resource;
      TransactionalBatchOperationResult post3t = batchResponse.GetOperationResultAtIndex(1);
      StackOverflowPost post3result = post2t.Resource;
   }
}

Here what it looks like when I debug this code and look at the result


4 comments:

  1. What if each document is using a different partionkey? Like document a key is 1, document b key is 2, and document c key is 3.

    ReplyDelete
  2. Can we have ACID transaction between CosMosDB and SQL Azure?.

    Can I say insert a row in SQL Azure and update one on CosMosDB and Commit or RollBack both with one command ?.

    ReplyDelete
  3. > If one of those inserts fails, you don't want to continue and abort the inserts.
    You probably wanted to say: "you want to _rollback_ successful inserts"? Because "abort" is pretty different from "rollback".

    ReplyDelete
  4. Sorry, but this example doesn't really demonstrate anything related to support of real ACID transactions in CosmosDB. Specifically, I would love to see if transactions are:
    1) A - atomic, i.e. "all or nothing", which means ability to rollback partial changes if exception happened midway. It is troubling that API doesn't have methods to perform "commit" or "rollback" explicitly, but it least show me implicit rollback actually works
    2) I - isolated, i.e. other "connections" (if such thing really exists in CosmosDB) can't observe partial results of not yet committed transaction.

    So far the example merely shows ability to submit batch of operations. This functionality is very similar to the one available in Redis, except that there are no claims of supporting ACID transactions. Btw, for Redis "isolation" of Lua stored procedures is achieved by single-threaded implementation. I wonder how does CosmosDB execute its batch operations or JavaScript stored procedures?

    ReplyDelete