Understanding idempotency in a HTTP method context
The RESTful architecting style utilizes HTTP protocol to provide a lightweight web service implementation, compared with SOAP, which is more of a RPC running on HTTP. One of the indicators for difference is the usage of HTTP methods.
RESTful services think of resources, located by URI links. So normally the service endpoint would be a noun, stands for a specific resource, e.g. /customer/01, /item/02, /image/03 etc. And the HTTP methods can be applied respectively, GET for read, POST for create, PUT for change/create, DELETE for deletion. There’re more but let’s just focus on these.
Normally in a UI, user wouldn’t change anything until it’s displayed first, therefore, when a PUT request is triggered, it’s relatively easy to provide the whole set of properties of the resource, so that the change will be made. PUT method requires a full set of properties to be submitted, even if there’s only one property got really changed.
Now, what is idempotency?
It means multiple times of submission would end up with the same intended/side effect: the expected change on the resource.
Well, the first time submission of PUT actually changed the resource state, but all the attempts, including the first one would have the same effect. If we compare this with GET, we’ll see for GET method, not just all attempts would have the same effect, the first attempt wouldn’t change the resource state either. Or in other words, there’s no side effect at all. For that, there’s a name called nullipotent. It’s obvious that nullipotent is also idempotent. It’s like nullipotent = (idempotent without change).
There’s also a name called “safe method”, which means the execution of the method wouldn’t cause loss of properties, no matter how many times called. Up to now I haven’t found difference between nullipotent & safe method. It’s more like one is talking about the behavior, the other is talking about the effect, different angle but the same thing.
Let’s take one example. Both POST and PUT are capable of creating a resource, difference is PUT would first make sure the requested URI doesn’t exist, otherwise just update, while POST just go create a resource under the requested URI. Therefore, multiple POST requests would create multiple resources, while multiple PUT requests return the same resource. This makes POST not idempotent, but PUT idempotent.
Now, why is idempotent and safe method (nullipotent) important?
From HTTP RFC documents we can see safe method is to prevent harm from spiders or other automation. Putting authentication & authorization aside, if a GET method was used for unsafe services, like change properties, a spider would cause unexpected result. Idempotency is to prevent harm from retry of failed attempts. HTTP protocol is used in Internet environment, where networking wouldn’t be reliable all the time. There’ll be failed attempts or delayed attempts. If retry happened, there could be two or more attempts triggered, and this shouldn’t cause unexpected result on the resource.
This means, idempotency has its importance in distributed transaction management. With distributed computing, traditional transactions on DB is not possible, since the data storage is distributed, e.g. with MSA. Here the distributed transaction can be seen as to ensure a consistent state among several resources. If every operation on every resource is idempotent, it’ll be much easier to achieve this.
GET is nullipotent and idempotent. PUT & DELETE are idempotent. POST is neither.
Well, to be precise, if POST is used for resource creation, as HTTP/1.1 RFC suggested, it’s not idempotent. But in reality POST is often used to update resource or even delete resource as common practice. Not exactly proper usage, but there’s chance that idempotency case can be found in these implementations.
In order to make partial change easier, PATCH method was introduced. This implies that the service consumption is no longer merely from UI. If it’s not GET and PUT in a pair, sending a full set of properties would become difficult. PATCH allows sending over a description of change, instead of the full set of properties.
This makes PATCH special, that it depends on the change operation, whether a specific PATCH request is idempotent. One cannot judge just by the name of method. e.g. using operation “add” is similar as PUT, using “replace” is update, these are to be idempotent, but using “move” wouldn’t be idempotent.
Now, what’s is “same effect”?
Let’s consider cases like getting the server time, the latest blog of a given user. It’s “get the newest state of the resource”, while the state would change over time. If there’re such RESTful APIs and GET method was used, is it safe or idempotent? It doesn’t return the same result every time.
Let’s consider another case. A RESTful API to return current user name, a GET request got Tom. Then a PUT request changed the name to Jerry. The second GET would return Jerry for sure. Does this change the idempotency?
No. Because the first GET cannot be hold accountable for the change. The two GET requests are having the same effect: returning the current user name. So, if there’s no other things changing the resource, they would return the same result.
Let’s go back to the earlier cases, we can apply the same rule: if there’s no other things changing the resource, multiple requests should return the same result. What’s the other thing in server time case? Time clock. If the time clock stands still, the GET requests will return the same result. For the latest blog case, if there’s no new post, the GET requests will return the same result.
Since idempotency was invented to prevent harm from failed or delayed attempts, we can also use this approach to judge if it’s idempotent: if there’s a failed attempt, do you want automatic retry? Would the automatic retry cause unexpected result of resource? Note there could be multiple retries due to the networking situation.
Going this far, we might see idempotency is not really limited to RESTful or HTTP, but rather an important concept in distributed computing, to make sure nothing goes wrong with multiple identical requests.