Idempotent PUT in a concurrent environment

2019-05-03 09:45发布

问题:

Context

I have a REST API where multiple clients (applications) can update the state of a resource with PUT. For the example, this resource is a lamp that you can turn ON or OFF.

This resource is also automatically updated by the system when it detects that an electricity failure has occurs, leading to have a lamp in a BROKEN state. I want to made the distinction between BROKEN and OFF, a lamp in BROKEN can not be turn ON !

Problem

I use PUT method to do this, something like PUT http://address:port/my_lamp { "state": "ON"}

But I am not sure if i respect the idempotent property of PUT method. In fact, i have 3 cases:

  • The lamp is ON. The above code leads to the ON state.
  • The lamp is ON. The above code leads to the ON state.... cool! At this moment, idempotency is still guaranteed :-) !
  • The lamp is BROKEN. The above code leads to an error, like 503 Service Unavailable

Question

I am not sure to correctly understand the notion of idempotency. Trust me, I read a lot of thing about it but still a little bit confused.

In my understanding, multiple PUT always leads to a same state of the resource: not guaranteed in my case due to BROKEN

But I could also understand it in an other way: multiple PUT always leads to the same side-effect: guaranteed, my request either produce to turn ON, either nothing (for the BROKEN case, it was already in).

EDIT:

I mean: the only side-effect is to turn ON the lamp, which is guaranteed (it either turn-on or do nothing here)

See here: Is REST DELETE really idempotent?

Which one is correct ? Depending of the understanding, my REST API ensure idempotency or not...

EDIT2:

From the definition of the W3C

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

Can i consider that it's an error to turn ON the lamp when it is BROKEN ?

回答1:

Idempotency means that in an isolated environment multiple requests from a same client does not have any effect on the state of resource. If request from another client changes the state of the resource, than it does not break the idempotency principle. Although, if you really want to ensure that put request does not end up overriding the changes by another simultaneous request from different client, you should always use etags. To elaborate, put request should always supply an etag (it got from get request) of the last resource state, and only if the etag is latest the resource should be updated, otherwise 412 (Precondition Failed) status code should be raised. In case of 412, client is suppose to get the resource again, and then try the update. According to REST, this is vital to prevent race conditions.

To elaborate even more:-

According to W3C(http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html), 'Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.'

Get request - {'state': 'ON'} Etag-header(say)- 123 PUT request - {'state': 'OFF'} Etag-header - 123

Some internal activity changes state such that new state is {'state': 'BROKEN'}. In this even etag should be changed to say 124.

put request - {'state': 'ON'} Etag-header - 123. Since etag header has changed, 412 error is returned which does not break idempotence of api (aside from error or expiration issues).

Get request - {'state': 'BROKEN'} Etag-header - 124 Put request - {'state': 'ON'} Etag-header - 124