How to handle Transaction in CosmosDB - "All or nothing" concept
Asked Answered
D

3

10

I am trying to save multiple Document to 'multiple Collection' at once in one Transaction. So if one of the save fail, then all the saved document should RollBack

I am using SpringBoot & SpringData and using MongoAPi to connect to CosmosDB in Azure. I have read in their portal that this can be done by writing some Stored procedure. But is there a way we can do it from code like how spring have @Transaction annotation.?

Any help is really appreciated.

Drobman answered 1/4, 2019 at 4:10 Comment(2)
check here....towardsdatascience.com/…Dander
FYI: Azure Cosmos DB now supports full ACID compliant transactions within the same logical partition key. Documented here: devblogs.microsoft.com/cosmosdb/…Elswick
H
7

The only way you can write transactionally is with a stored procedure, or via Transactional Batch operations (SDK-based, in a subset of the language SDKs, currently .NET and Java) . But that won't help in your case:

  • Stored procedures are specific to the core (SQL) API; they're not for the MongoDB API.
  • Transactions are scoped to a single partition within a single collection. You cannot transactionally write across collections, regardless whether you use the MongoDB API or the core (SQL) API.

You'll need really ask whether transactions are absolutely necessary. Maybe you can use some type of durable-messaging approach, to manage your content-updates. But there's simply nothing built-in that will allow you to do this natively in Cosmos DB.

Hydrosome answered 1/4, 2019 at 4:49 Comment(2)
I don't believe this answer is correct (anymore) as transaction are supported nowadays. See https://mcmap.net/q/1078021/-how-to-handle-transaction-in-cosmosdb-quot-all-or-nothing-quot-conceptyMelioration
@MartinWickman - you are correct - transactions can now be performed without the use of stored procedures - (though they are still limited to a single partition within a single container). I'll update the answer soon.Hydrosome
P
4

Transactions across partitions and collections is indeed not supported. If you really need a Rollback mechanism, it might be worthwhile to check the event sourcing pattern, as you might then be able to capture events instead of updating master entities. These events you could then easily delete, but still other processes might have executed using incorrect events.

We created a sort of unitofwork. We register all changes to the data model, including events and messages that are being sent. Only when we call a committ, the changes are persisted to the database, in the following order:

  • Commit updates
  • Commit deletes
  • Commit inserts
  • Send messages
  • Send events

Still, it's not watertight, but it avoids sending out messages/events/modifications to the data model as long as the calling process is not ready to do so(i.e. due to an error). This UnitOfWork is passed through our domain services to allow all operations of our command to be handled in one batch. It's then up to thé developer to realize if a certain operation can be committed as part of a bigger operarion(same UoW), or independent(new UoW).

We then wrapped our command handlers in a Polly policy to retry in case of update conflicts. Theoretically though we could get an update conflict on the 2nd update, which could cause an inconsistent data model, but we keep this in mind, when using the UoW. It's not watertight, but hopefully it helps!

Protestation answered 10/4, 2019 at 5:43 Comment(0)
M
2

Yes, transactions are supported in Cosmos DB with the Mongo API. It believe it's a fairly new addition, but it's simple enough and described here.

I don't know how well it's supported in Spring Boot, but at least it's doable.

// start transaction
var session = db.getMongo().startSession();
var friendsCollection = session.getDatabase("users").friends;
session.startTransaction();
// operations in transaction
try {
    friendsCollection.updateOne({ name: "Tom" }, { $set: { friendOf: "Mike" } } );
    friendsCollection.updateOne({ name: "Mike" }, { $set: { friendOf: "Tom" } } );
} catch (error) {
    // abort transaction on error
    session.abortTransaction();
    throw error;
}

 // commit transaction
 session.commitTransaction();
Melioration answered 17/12, 2021 at 14:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.