Idempotent PUT in a concurrent environment
Asked Answered
M

1

6

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 ?

Macymad answered 10/6, 2015 at 15:22 Comment(0)
W
11

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
Wrongdoer answered 11/6, 2015 at 8:3 Comment(7)
(see my edit2). What do you mean by "does not have any effect on the state of resource" ? That's where i am confused. Notice also that the update of the state to BROKEN is made by the system itself... then it doesn't need to do it through the interface REST. Even if the client get a 412 in my case, if he does again the same request with the correct etag... it will still get 503 - Service Unavailable. So we are back with the same problem.Macymad
What I mean is if some event other than your put request updates the resource, than that does not mean idempotency principle is broken. It only means that in a single user environment (where no one else can change the resource) the state of resource should not change due to multiple requests. Also, If state of resource changes due to any reason, the client is suppose to first get the updated resource and then send the put request (that's why etag stuff).Wrongdoer
Ok thanks! So basically, as the state of my lamp resource change only in ON when doing a PUT ON, or OFF when doing a PUT OFF... aside from different error that leads to not change the state of the resource (503, here)... I can consider that my implementation of PUT is idempotent ?Macymad
Yup. But it would be great if you could edit the last put request with the correct etag header.... That leads to 503, then does not break idempotency (same reason that 412 error)Macymad
I think it is already correct. Client would try to attempt put again with etag it already has, only then it would realise that etag has already expired.Wrongdoer
I was talking about the response of the last put request (with the valid etag: 124). Assuming that you cannot reach the state ON from the state BROKEN, the server will respond an error (5xx whatever).Macymad
I think that's precisely what makes PUT idempotent in my case. If this last put would leads to an other state (say "ON_LOW_ENERGY", because the lamp works with a small battery)... it would be no more idempotent.Macymad

© 2022 - 2024 — McMap. All rights reserved.