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.
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.
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.
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.
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.
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)
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. |
ETags
in a result entityThe 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.
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.
Let's consider some of the optimistic locking techniques that are not specified in Zalando.
and
version number in
result entitiesIn 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.
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.
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.
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 since
Last-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.
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?
Zalando Science Student Diary RFC7232
Recommended Posts