In this blog post we are going to look into optimistic concurrency control in Web API c# .Net core project.
Allows the system to produce multiple independent transactions without adding locks on CRUD in the data storage level. Before committing each transaction, the server-side check is run to see if any changes are done for an existing model in data storage to prevent the possible overriding threat. If no changes are made in the current model, the transaction will be completed. Otherwise, a notification from the server-side will be received that another transaction has already updated data, and it will not be allowed to overwrite the current resource unless an agreement is given to it.
The HTTP protocol does not have a native solution for Optimistic Concurrency Control. The most common implementation of OCC is by using the ETag (Entity Tag) header. For example, a user uses HTTP GET to receive resources from your endpoint. As a response, you are getting your entity and headers array. That array contains an ETag header, which represents the current resource. After receiving and modifying resources, the user wants to apply local changes to the server storage facility. In this case, he needs to execute HTTP PUT into a specific resource and add value received from the ETag header into the If-Match header. The server, in this case, gets the If-Match header and tries to compare it with the current resource. If the header contains the same information as the current resource, then received changes are applied to it. Otherwise, if header information differs from an existing resource, it returns a message that the model is already modified, please look for existing changes.
Short command list for creating new dotnet project using terminal (command line):
dotnet new [templateName] -o [outputDirectory] – creates a new project, configuration file, or solution based on the specified template.
dotnet restore – restores the dependencies and tools of a project.
dotnet build – builds a project and all of its dependencies.
dotnet clean – cleans the output of a project.
dotnet run – runs source code without any explicit compile or launch commands.
You can find more commands and useful information regarding dotnet command in the official site.
Before starting, you can find complete code at my GitHub repository.
Fast forward to the phase where you already have created project (or just cloned/forked my GitHub repository), a class for querying data (let's call it a repository), and a basic API for data manipulation (Applying CRUD). The next thing you want to add is OCC behavior to your API project.
In the first place, you need to figure out your hashing algorithm for creating object hashes. In the given example we are using this kind of algorithm – Serializing object into JSON string and computing SHA512 hash out of that string
In real-life scenarios, you may want to add so-called SALT or even calculate a hash from some entity properties. Don’t forget that your hash needs to be updated when the object is changed!
You need to add it as an ETag header (it also can be used for caching purposes). You could use the mini helper method for doing it:
Here is an example of using these c# methods in controller endpoint for getting specific resource:
After checking if the resource exists in the storage facility, the controller creates a hash of the received resource and adds it as an ETag header, then returns the resource itself with status code 200, also known as Ok.
Let's dive into a more interesting use case – Update:
At the beginning controller checks if needed header (If-Match) is presented. If not, it returns a bad request with an error message.
Then checks if the received object hash differs from the received hash in the header. If hashes are the same, it will return a 304 HTTP status code that says “Not Modified” because the system would think that the user is spamming the save button and sending the same object as received. We are checking this case first to minimize traffic towards the storage facility.
The last check is dedicated to looking into the database and saying if the model is the same as when the client began the editing action. Controller gets the resource from databases and computes hash from it. Then tries to compare the calculated hash with the hash specified by the header. If those two hashes are precisely the same, then the controller is going to initiate update action for the existing resource; otherwise, it will return a 409 status code that stands for “Conflict” and this status code will indicate that the resource is already updated by other use/system.
In the beginning, let's create a new vacation resource. Just do a POST action into the "api/vacation" endpoint:
The server gives a response:
Then let's create three PUT requests with the If-Match header that contains the received hash from the ETag header.
Exactly the same as it was received:
After executing this request server gives us this response which says that the model that has been sent is not modified:
The second request is going to send a different body to update the current resource
And the server is giving us 200 status code, updated resource, and newly compiled hash:
Now let's send a request for updating resource with the old hash code:
And the server will respond with 409 – there is a conflict between the hash you are sending and the hash computed by using the newest resource. That means someone has already modified the resource, and you need to reload your model with the latest data.
You can find all code that was used for this blog post in my GitHub repository https://github.com/grayMou5e/optimistic-concurrency-control