Handling concurrent access is quite common in transactional data managing systems. As long as your application only creates and reads data records concurrency is not compelling. But as soon as you modify data – which e.g. happens when updating or deleting an existing record – you need to decide how to handle this.

The easiest approach is “the last change wins”. This implies you just ignore any change that might have taken place between reading and updating the record. However this leads to the so called “lost update problem” – which for must business scenarios is obviously not applicable.

Therefore we need a more advanced solution how to handle concurrent access. Hence there are two possible approaches:

  • Pessimistic concurrency control: The system locks the data record as long as a client works with it and prevents any modification by another client.
  • Optimistic concurrency control: Together with the data itself a versioning token is given to the client. When trying to modify the record the systems checks if this token is still valid, which means no change took place in the meantime. The change is only applied if no conflict is detected.

Typically distributed systems are stateless, thus the pessimistic solution does not really fit. One reason for this is that most distributed systems use REST or at least HTTP for communication – which itself is already stateless. Therefore the optimistic concurrency control is the preferred approach.

In this post I will describe how optimistic locking with RESTful communication is implemented. Spring Boot is used for the back end (micro-)service and as a client – or so to say service consumer – we’ll use an Angular 2 web front end.

Usage of HTTP Headers and Status Codes

Preliminary remark: For reasons of simplicity the following steps focus on how to handle updates (done with the HTTP verb PUT). Nevertheless the same principle may be adapted for deletions (done with DELETE) or other non-safe modifications (e.g. POST or PATCH).

As already mentioned for optimistic concurrency control we need a way to provide the clients with tokens containing versioning information of the data they receive.
Like most modern distributed systems we use the de facto standard for synchronous communication: The RESTful application paradigm. And generally speaking REST is done via HTTP. So it is worth to have a closer look if HTTP provides any mechanism that might be suitable to handle these tokens.

And – good news – HTTP1.1 comes with an appropriate header field called ETag:
“The ETag or entity tag is part of HTTP […]. ETags can also be used for optimistic concurrency control, as a way to help prevent simultaneous updates of a resource from overwriting each other.” (see Wikipedia)

Accordingly if a REST client GETs a resource the back end service sends the versioning information in the response header field ETag:

Getting the ETag HTTP response header

The ETag may contain a hash value of the resource’s content, a hash of the last modification timestamp or even just a revision number.

The service consumer must cache the received versioning information and when calling a PUT operation add it to the request header field If-Match.

Setting the If-Match HTTP request header

Basically the If-Match header makes the request conditional. In our case the back end service must check if the condition “resource was not modified” is fulfilled.

If the condition is satisfied, that means the update is possible, the HTTP status code 200 (OK) with the updated resource as payload is sent (or alternatively 204 (No Content)).
If however the update cannot be applied because the resource was modified in the meantime the service will return the status code 412 (Precondition Failed). Receiving that status the client has to decide how to handle the conflict – by reloading the resource and discarding or merging the changes.

Remark: The HTTP header fields ETag, If-Match and If-None-Match are also used for web cache validation, what will not be described in this blog post (further details).

Usage of the JPA Annotation @Version

So far we have seen how the versioning information is transmitted between service provider (our back end) and the service consumer (our client). But where do we get this information?

A common approach is to use a collision-resistant hash function of the resource’s content. This generates a value like shown in the screenshots above.

But if the data is persisted in a relational database using an ORM framework like Hibernate (or generally speaking the Java Persistence API (JPA)) we may retrieve the revision number in another way:
JPA provides the @Version annotation to mark a technical attribute for managing a dedicated revision number (see line 4-6):

Basically the entity’s version attribute and the associated database column are managed by JPA and are used to detect conflicting updates. Therefore JPA already takes care of incrementing the revision number with every update:

The version column managed by JPA

We will go into detail about this in the next chapter. For now let’s just capture that this revision number is exactly what we need as token to hand out to the client via the ETag header.

The following sequence diagram sums up the described usage of the HTTP headers and status codes together with the entity’s version:

Implementing the service provider with Spring Boot

Well, enough theoretical explanations – let’s start implementing.
Our service providing back end is implemented with Spring Boot. More specifically we have a Spring MVC RestController that adds and extracts the required HTTP headers and does the update condition check.

Setting the ETag response header field

When looking for ETag support in Spring you’ll find the ShallowEtagHeaderFilter. This filter may easily be added as a configuration to your Spring Boot application:

The filter adds an ETag header to all GET responses containing a hash value of the resource’s content:

The shallow ETag value generated by the Spring filter

Looks quite convenient, doesn’t it? But unfortunately this is only a shallow implementation of the ETag mechanism. It is sufficient for web cache validation using the If-None-Match header.
But what we need is support of the If-Match header and this has not yet been implemented in Spring. So unluckily this filter won’t help and we have to implement the mechanism ourselves.

The good news, though, is as we wanted to use the entity’s version anyway setting the ETag header in the RestController method is pretty easy:

In line 3 the Spring Data Repository reads the Book entity for the requested identifier. The Book’s version attribute is then set to the ETag response header field (see line 9). We just add two double quotes to make the value compliant to the HTTP specification.

When performing a GET the response header field now contains the Book’s version:

Getting the entity's version as ETag value

Extracting the If-Match request header and checking the constraint

All right, we are now able to send the revision number within the ETag to the client. The client returns the value when trying to update the resource with a PUT operation.

Therefore we have to extract the transmitted value and see if our update constraint is fulfilled.
The steps that need to be realized are shown in the following activity diagram:

Activity diagram how to implement the optimistic locking

First of all we need to extract the value from the If-Match request header field. If the PUT request does not contain the header it is not valid, therefore the HTTP status code 400 (Bad Request) is returned.
In case the value is found it is compared to the latest version of the entity. If these values don’t match the status code 412 (Precondition Failed) is sent and processing is done.
As soon as the two values are the same the resource is updated.
Even after comparing the received version with the current version – that means after reading the stored record from the database and before updating it – there might be another transaction that changes the record in the very same moment. Therefore it is still possible that an OptimisticLockException occurs. In this case the HTTP status code 409 (Conflict) is returned.
Supposing that the update is executed correctly the status code 200 (OK) is sent to the client.

The code fragment below shows the realization of the Spring MVC RestController method:

In line 8 the If-Match value is extracted. If it is found it is matched against the Book’s current version – complemented with the ETag specific double quotes (line 12).
If the values differ the status code 412 is returned. If however the values are identical we try to save the updated Book entity (line 16-19).
The updated Book resource is added to the payload of the response and sent with the status code 200.
The OptimisticLockException that might be thrown by JPA/Hibernate is caught and mapped to the status code 409 (line 24-26).

Further consideration

Of course this example just shows the prototypical implementation. When adapting it for a real-life scenario I suggest the following optimizations:

  • Move the cross-functional aspect of adding and extracting the HTTP header fields to a base class or HandlerInterceptor
  • Add a service layer instead of calling the Spring Data Repository straight from the RestController
  • Shift the exception handling to an ErrorController
  • Return more specific error messages in the response’s payload

Finally two comments concerning our Spring Boot implementation:

  1. Instead of doing the comparison of the header field yourself it is worth having a look if Spring already provides a build-in mechanism.
    At first glance the method ServletWebRequest.checkNotModified(String etag) is exactly what we’re looking for. Then again – like mentioned above – Spring so far only manages the If-None-Match header and the If-Match header is not taken care of yet. Therefore this method is unfortunately not usable until now.
  2. When returning the status code 200 and the updated resource in the PUT response’s payload you have to provide the ETag of the updated resource as well (see line 22 in the code fragment above). Unluckily this is only possible if using at least Spring Boot version 1.4.3. For example with version 1.4.1 setting the ETag for PUT and POST operations is ignored, only GET is processed appropriate.

Implementing the service consumer with Angular 2

As stated previously the client must cache the versioning information received in the ETag response header and when calling a PUT operation return it using the request header field If-Match. In this last chapter I’ll show how this is done using an Angular 2 front end.

Handling the received ETag header value

For dealing with the received data we create a resource class – which behaves almost like a DTO. This BookResource would look like this:

Please note the technical attribute eTag which will be used for caching the received value (in line 7).

The API layer of the Angular 2 service consumer uses the Angular Http client to perform the REST operations. Therefore we create a class ApiService containing the method to GET a single Book from the back end service:

Line 7-9 shows the implementation of the method itself.
Parsing the response to JSON and mapping it to the BookResource is done in the helper method mapResponse (line 13-23). So far this approach corresponds to the Angular documentation. The specificity lies in line 21 where we extract the value from the ETag response header and add it to the JSON string so it is mapped to our BookResource.
Remark: Of course the error handling (line 15-17) has to be improved for a real-life application.

Calling this method can be done like this:

Returning the versioning information

So far we have read the ETag value and cached it in the BookResource. Now when calling the PUT operation we need to send it back. Therefore we add a put method to the ApiService:

In Line 3 you can see how the value – which is handed over as parameter – is added to the If-Match request header.

To call this method you have to provide the eTag parameter, which can easily be selected from the BookResource:

That’s it, we have extracted, cached and returned the versioning information.

Conclusion

In this blog post I showed how distributed optimistic locking using REST, Spring Boot and Angular 2 may be done.
Summarizing I would like to emphasize the importance of the HTTP header fields ETag and If-Match. Even if another back end or client framework is used this mechanism still provides a standardized way to transmit versioning tokens between service provider and consumer.

Subsequent one last remark concerning the distributed communication: I described how an Angular 2 client may handle the ETag information. But of course the same applies for any other client – even if the service consumer is another microservice. That means if you have synchronous RESTful communication between your microservices the described pattern might easily be adapted. However I doubt if synchronous communication between microservices is a good practice. I would rather suggest doing this in an asynchronous manner. But that discussion is out of scope of this blog post.

Leave a Comment

4 comments

  1. w

    I wish there were more posts like this one. I especially appreciate the concise explanation and the effort you’ve put in preparing these elegant diagrams.

  2. C

    Thank you!
    Good to know that the time spent for creating the diagrams was invested well. 🙂

  3. V

    Good post about versioning! Thank you for ETag

  4. C

    You’re very welcome! 🙂

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close