How to implement optimistic locking in REST API

Overview

Changing the state of a resource is an operation implemented on any system. At this time, considering the guarantee of integrity, some kind of locking mechanism is required.

Here, referring to Zalando, we will ensure consistency by optimistic locking when updating the RESTful API. Let's see what the idea is in that case. After that, we will consider what would happen if optimistic locking was actually implemented, and the parts that are not clearly stated in Zalando.

How to implement optimistic lock

The Zalando RESTful API Guide (https://restful-api-guidelines-ja.netlify.com/#optimistic-locking) provides four ways to achieve optimistic locking:

Let's see how these four achieve optimistic locking in the case of getting list information via API and updating one of them.

If-Match header and ETag header

The update flow and optimistic lock flow are shown in the figure below. Return with ʻEtag header Check the match / mismatch between the value returned to the client side in the part and the value given to ʻIf-Match in Update with the ʻIf-Match header, and see the API server Is deciding whether to update.

If-Match-and-Etag.png

ETags in the result entity

The update flow and optimistic lock flow are shown in the figure below. Use Return list to get all the resources associated with the corresponding endpoint. At that time, set the value of each resource to seed, create an etag with some algorithm, and include it in the response. In API server, update is possible depending on whether the value given to ʻIf-Match in update with the ʻIf-Match header is the same as the recalculated etag value of the resource for which the update was requested. To judge.

結果エンティティにおけるETags.png

Version number

The update flow and optimistic lock flow are shown in the figure below. The client returns a list including the version number, and acquires all the resources associated with the corresponding endpoint in the form including the version number. The API server determines whether or not the update is possible based on whether the version number sent by update and the version number of the resource for which the update was requested are the same. If an update is made, the version number of the corresponding resource will be incremented.

バージョン番号.png

Last-Modified / If-Unmodified-Since The update flow and optimistic lock flow are shown in the figure below. The client returns the list with the Last-Modified header and gets all the resources associated with the corresponding endpoint. At that time, add the last modified date and time of the resource in the Last-Modified header. In API server, the date and time sent by ʻIf-Unmodified-Since (= Last-Modified` value) is compared with the last modification date and time of the resource for which the update request was made, and whether or not the update is possible is determined. .. (If the last update date and time of the resource is newer, it is judged that the update is not possible)

Last-Modified_If-Unmodified-Since.png

Pros / Cons of each method

How to achieve optimistic lock merit Demerit
If-Match header and ETag header ・ RESTful solution ・ The number of requests increases
ETags in the result entity ・ Perfect optimistic rock -Information to be added to the HTTP header gets into the business object.
Version number ・ Perfect optimistic rock -Information to be added to the HTTP header gets into the business object.
Last-Modified / If-Unmodified-Since ・ Because it has been used for a long time, it has a proven track record.
-Does not interfere with business objects
・ Easy to implement
・ No need for additional requests other than update requests
-If the API side is composed of multiple instances, strict time synchronization is required.

A concrete example of optimistic locking with ETags in a result entity

The following source implements list acquisition and update with the endpoint ʻoptimistic / etag`. https://github.com/nannany/optimistic-lock-demo ETag generation is done with sha256 as follows.


package nannany.optimistic.demo.util;

import com.google.common.hash.Hashing;

import static java.nio.charset.StandardCharsets.UTF_8;

public final class DemoUtils {

    @SuppressWarnings("UnstableApiUsage")
    public static String getHash(String origin) {
        return Hashing.sha256().hashString(origin, UTF_8).toString();
    }
}

As shown in the gif below, if the ETag values match, the update is successful, and if they do not match, 409 is returned.

etag2019-09-23 17-43.gif

A concrete example of optimistic locking by Last-Modified / If-Unmodified-Since

The following source implements list acquisition and update with the endpoint ʻoptimistic / lastModify`. https://github.com/nannany/optimistic-lock-demo The date and time when the list was acquired is entered in the last update date and time, and optimistic locking is performed depending on whether the resource has been updated since then.

As shown in the gif below, if the last modified date and time of the resource to be updated is before the date and time received in the ʻIf-Unmodified-Since` header, the update is successful, otherwise 412 is returned.

lastModify2019-09-23 17-55.gif

Consideration

Let's consider some of the optimistic locking techniques that are not specified in Zalando.

Differences between ETagsandversion number in result entities

In Zalando, both of the above two methods Benefits: Perfect optimistic rock Disadvantage: Information to be added to HTTP header gets into business object There is no clear difference between the advantages and disadvantages. (I'm not sure about the perfect optimistic rock because there is no detailed explanation)

We believe that the differences between the advantages and disadvantages of these two methods are the following two points.

Should the API server retain the element to detect optimistic locks?

In both methods, the list information returned to the client contains elements (ETag and version) outside the business object. However, on the API server side, the ETag value can generate some value of the business object as a seed value, that is, it is not necessary to hold the ETag value, while the version value is held on the API server side. Must be done. On the other hand, if you decide not to keep the ETag value on the API server side, you have to calculate the ETag each time you get the list information, so you need to consider whether it is okay in terms of performance. That's right.

When thinking about how to simplify the persistence information and what to do with the performance, I think this is a merit and demerit.

Difficulty of analogy of the value to judge whether to update

The version number is basically a value that is incremented by 1 with each update, and it is relatively easy to guess the version number that can be updated. On the other hand, inferring the ETag value requires the algorithm used to generate the ETag, what seed is used when generating the ETag, and the seed value when updating, which is more difficult than analogizing the version number. It is considered.

When thinking about security, I think this is a merit and demerit.

About the security aspect of Last-Modified / If-Unmodified-Since

In Last-Modified / If-Unmodified-Since, if you rewrite the value of the ʻIf-Unmodified-Sinceheader from the client side and specify the future far ahead (such as December 31, 9999), the resource Even if the value has been updated sinceLast-Modified`, it will be possible to update from above.

Therefore, from a security perspective, I don't think Last-Modified / If-Unmodified-Since is strong.

Summary

I looked at the optimistic locking method listed in Zalando, but in the end I thought I had no choice but to think of a method that should be taken according to the requirements of the system I was building. The fact that Zalando also introduces four methods means that the best optimistic lock implementation method has not been devised for every system.

However, instead of thinking about how to implement the optimistic lock of RESTful API from 0, it is recommended to implement it while referring to the description of Zalando etc. and applying the introduced advantages and disadvantages to your system and investigating it. Isn't it a realistic solution?

reference

Zalando Science Student Diary RFC7232

Recommended Posts

How to implement optimistic locking in REST API
Implement REST API in Spring Boot
How to implement date calculation in Java
How to implement Kalman filter in Java
How to implement coding conventions in Java
How to implement ranking functionality in Rails
How to implement asynchronous processing in Outsystems
How to implement a job that uses Java API in JobScheduler
How to implement a like feature in Rails
How to implement Pagination in GraphQL (for ruby)
How to implement UICollectionView in Swift with code only
How to implement guest login in 5 minutes in rails portfolio
How to implement a like feature in Ajax in Rails
How to store Rakuten API data in a table
Introduce swagger-ui to REST API implemented in Spring Boot
Summary of how to implement default arguments in Java
How to use Chain API
Introduction to EHRbase 2-REST API
[Rails] How to implement scraping
[Java] How to implement multithreading
How to implement infinite scrolling (page nate) in Swift TableView
How to call and use API in Java (Spring Boot)
How to implement one-line display of TextView in Android development
Let's find out how to receive in Request Body with REST API of Spring Boot
How to use Lombok in Spring
How to find May'n in XPath
How to hide scrollbars in WebView
How to iterate infinitely in Ruby
Try to implement Yubaba in Ruby
How to run Ant in Gradle
How to master programming in 3 months
How to learn JAVA in 7 days
How to install Bootstrap in Ruby
How to use InjectorHolder in OpenAM
How to introduce jQuery in Rails 6
[Rails] How to implement star rating
How to use classes in Java?
How to return Rails API mode to Rails
How to name variables in Java
How to set Lombok in Eclipse
Try to implement Yubaba in Java
How to concatenate strings in java
How to install Swiper in Rails
[Behavior confirmed in December 2020] How to implement the alert display function
[RubyOnRails] How to implement pull-down in form_with based on table data
How to implement UI automated test using image comparison in Selenium
[swift5] How to specify color in hexadecimal
How to create your own headless API using Liferay's REST Builder (Part 3)
How to create your own headless API using Liferay's REST Builder (Part 2)
Multilingual Locale in Java How to use Locale
How to change app name in rails
Implement API Gateway Lambda Authorizer in Java Lambda
How to use custom helpers in rails
How to reflect seeds.rb in production environment
How to use named volume in docker-compose.yml
Try to implement n-ary addition in Java
How to filter JUnit Test in Gradle
How to insert a video in Rails
How to include Spring Tool in Eclipse 4.6.3?
How to add jar file in ScalaIDE
How to implement more than one top page in Rails breadcrumb trail