[Available via NuGet – MongoDB.Kennedy.Concurrency]
[Available via GitHub – optimistic_concurrency_mongodb_dotnet]
This article demonstrates a technique and supporting library for adding optimistic concurrency control to NoSQL databases and MongoDB in particular.
Watch a video walk-through using this library:
Quickly, what is optimistic concurrency control?
Ideally, all databases that allow concurrent access or disconnected access need to implement some form of concurrency control. This usually comes in two flavors:
Pessimistic concurrency control is usually used when working heavily within transactions. That may be fine for bank transfers, but it typically falls down in the face of disconnected models used by almost all ORMs such as Entity Framework. Moreover, it is entirely inappropriate for NoSQL databases.
Frameworks such as Entity Framework have optimistic concurrency control built in (although it may be turned off). It’s instructive to quickly see how it works. Basically there are three steps:
- Get an entity from the DB and disconnect.
- Edit in memory.
- Update the db with changes using a special update clause. Something like: “Update this row WHERE the current values are same as original values”.
If that update returns “0 rows modified” then we know it was changed since we loaded it and are about to overwrite someone’s changes. This results in a concurrency exception and not changes go through.
Optimistic concurrency control for MongoDB
By carefully constructing update commands in C# with the official 10gen C# driver, we can achieve almost exactly the same flow. At the end of this article is a simple C# class (data context) which has save and delete methods which internally are safe via optimistic concurrency control.
The only thing you need to do to use this library in your apps is to implement this interface on all top-level MongoDB entities and use a class derived from ConcurrentDataContext (in library below) for your data access.
Using this interface, the data context will manage a unique ID for you named _accessId per save. If someone else edits this object after you have gotten it but before saving it, you’ll get a concurrency exception, just like EF, and no changes will go through. All you do is call save and access entities via LINQ queries. Nice huh?
Here is an example of your entities with this interface in the db:
Here is an example of an app running end to end with only one editor on the document. See, no errors:
And here is simulating a concurrent edit, which results in an error.
There you have it! For all of you who want to adopt MongoDB and .NET but are concerned about concurrency issues, you now have the same level of concurrency safety as Entity Framework. That should be quite reassuring.
Download for the source, library with the concurrent data context, the sample app, and unit tests from GitHub:
Really nice post! This library was mentioned in an excellent talk at DevWeek 2012 in London. If you are installing this library out of the box using NuGet, ensure that you add the latest versions of MongoDB.Bson and MongoDB.Driver.
Hi Great Work
How do I pass the MongoDB Credentials to the DataContext
When I attempt to pass it in the server name parameter it is telling me that is still trying to autenticate with the default admin user.
Your help will be really appreciated
Hi! Great Work
How do I authenticate or pass the MongoDb Credentails utilizing this library
I will really appreciate it! and Thanks
Thanks. This is something I need to add apparently. I opened a GitHub issue on it:
You can track it and get notifications there.
Michael, can you explain a bit more about how this works behind the scenes? For example, I dont understand how your library can work given its modifying the client driver only and not the core mongodb libraries – In any solution to the problem being addressed (concurrent updates) I would expect the server to have to check the etag/accessId whilst holding a lock on the object and throw/return a ‘ConcurrencyException’ to the callee if they do not match
The key thing my library leverages is that the server guarantees that updates to a single document are always atomic. There are no transactions or any multi-document promises, but single docs are basically transactional.
So it goes like this. Each doc has a changing accessId field. And so
Client 1 reads doc, has _id = 7 and accessId = 78472847
Client 2 reads doc, has _id = 7 and accessId = 78472847
Client 2 updates the doc using the query where _id = 7 and accessId = 78472847
One row modified, all good.
my lib changes accessId = 8594274 (as part of that update)
Client 1 updates the doc using the query where _id = 7 and accessId = 78472847
Zero rows modified (there is no such doc), concurrency error detected!
what should i do if i want to update a model?
Have a look at the video in the article. Should be pretty clear.
Micheal do you have any reference for how to handle optimistic concurrency with the new 2.0.1 C# driver?
I don’t yet, but I hope to add it soon. Should be conceptually the same idea.
Ok, I would be very interested when this becomes available
I would also be interested to see how this is implemented in the new driver. Great post!