What's the correct way to view idempotency in terms of HTTP DELETE?
Asked Answered
L

3

15

I have spent a lot of time recently reading the HTTP 1.1 specification and relating it to REST. I have found that there are two interpretations of the HTTP DELETE method in regards to its "idempotency" and safety. Here are the two camps:

  1. If you delete a resource with HTTP DELETE, and it succeeds (200 OK), and then you try to delete that resource N number of times, you should get back a success message (200 OK) for each and every one of those delete calls. This is its "idempotencyness".

  2. If you delete a resource with HTTP DELETE, and it succeeds (200 OK), and then you try to delete that resource again, you should get back an error message (410 Gone) because the resource was deleted.

The specification says DELETE is idempotent, sure, but it also says that sequences of idempotent events can still produce side effects. I really feel like the second camp is correct, and the first is misleading. What "safety" have we introduced by allowing clients to think they were the cause for deleting a resource previously deleted?

There are a LOT of people in the first camp, including several authors on the subject, so I wanted to check if there was some compelling reason other than emotions that lead people into the first camp.

Lotetgaronne answered 12/4, 2009 at 2:26 Comment(2)
With your #2, i think you should take out "recently" the recentness of the deletion shouldn't have anything to do with the strategy of this response.Bufordbug
@JeffMartin You're right, it was more conversational than factual. Removed.Lotetgaronne
Y
22

Being idempotent does not mean that a request is not allowed to have side-effects (that's what the 'safe' property describes). It just mean that issuing the same request multiple times will not result in different or additional side-effects.

In my opinion, the subsequent DELETE request should return an error - it's still idempotent because the state of the server is that same as if only one DELETE request were made. Then again returning the 200 OK status should be OK as well - I don't think being idempotent requires the returning of an error code for the subsequent DELETE requests - it's just that returning the error status seems to make more sense to me.

Yaelyager answered 12/4, 2009 at 2:51 Comment(7)
I have the same opinion, I'm glad to see someone else shares it. I guess I just needed to hear it. Thanks. I've got quite a few books on the subject and a surprising number are treating idempotence as if it were also safe, and it isn't.Lotetgaronne
As an opposing view point, I feel like a DELETE request is more along the lines of "Make sure this resource is deleted/removed". Whether it exists at the time of the request doesn't really matter to the success status of the response. If two different clients send the DELETE request "at the same time" why should 1 get an error response and the other success, when in effect both successfully carried out the goal.Bufordbug
@JeffMartin You could go that way, but I'd say the reason the second one receives an "error" is that because the first request got there first. It's more accurate. In some systems, it matters which was the first to perform the deletion and which resulted in a no-op due to a missing resource. If your system is just ensuring non-existence then your concept works. A non-200 is just a non-200 though, it's not an error, it's a result. Some frameworks treat it like an exceptional case, which might lead to thinking that a non-200 is "bad".Lotetgaronne
@DanielCrenna I agree with the principle of what you are saying. I think there are some cultural issues around those 400 responses that do lead to frameworks forcing exceptions. I think we are going with a nice compromise here: blogs.msdn.com/b/cellfish/archive/2012/04/02/…Bufordbug
I'm with @JeffMartin more, I don't see what exactly the client is suppose to do with the information that an item was already deleted before its request to delete it. That could either mean it sent the request multiple times or that some other client deleted it first. But if the client does really do something different then that means the state transfered from server CHANGED from the first DELETE to the second, it's the response (return of the function) that matters because the state on the server obviously changes after the first request.Acolyte
This is insane. If your intent is to delete an existing object via an identifier, then it should be a clear failure if that resource doesn't exist or has already been deleted. Calling delete {bogus_id} should NEVER succeed. In other words, if there's no way to know whether {bogus_id} was ever valid, it should fail.Renelle
On the other hand, idempotency makes sense ONLY if you can assert that the id was valid, such as if you're not actually deleting the resource but marking it as deleted. In that case, the end result is the same and so it would make sense for the delete call to return successful when called multiple times on a previously existing resource. But as I said, calling delete with an invalid id (i.e. for a resource that no longer or never existed) makes no sense to return success.Renelle
A
2

@MichaelBurr is correct about idempotency and side-effects.

My opinion is that there are 2 states involved in a given REST request, the client's state and the server's state. REST is all about transferring these states between the server and the client, such that the client's state maps to a subset of the server's state, in other words, the subset stays consistent with the server. Because of that idempotency should mean that subsequent idempotent requests will not result in either state being different than it would be from only making the request once. With the first DELETE you would imagine that the server deletes the resource and lets the client know it can delete the resource as well (as the resource "doesn't exist anymore"). Now both states should be identical to before with minus the item that was deleted. For the client to do anything different when it tries to delete the item after it has already been deleted, then the state that is transfered from the server to the client must contain different information. The server can do things slightly differently with the information that the resource was already deleted, but once it responds with something different idempotency of the methods is essentially broken.

For idempotent function:

delete(client_state) -> client_state - {item}
delete(delete(client_state)) -> client_state - {item}
delete(client_state) = delete(delete(client_state))

The best way to guarantee this idempotency is if the server's response is identical, that means the only way for the client's state to break the idempotency is for there to be non-determinacy or side effects in the client's handling of the response (which probably points to an incorrect implementation of handling the response).

If there is an agreement between the client and server that the status codes exist outside of the representation of the state being transferred (REST), then it is possible to inform the client that the item "doesn't exists anymore" (as it would in the first request) with the extra comment that it had previously been deleted. What the client does with this information is unclear, but it shouldn't effect the resulting client state. But then the status code can't be used to communicate state, or rather if it does also communicate state in other situations (like maybe "you don't have permission to delete this item" or "item was not deleted"), then there's some introduced ambiguity or confusion. So, you at least need a pretty good reason for introducing more confusion into the communication if you want to say that DELETE is idempotent and still have the server's response depend on previous DELETE requests that are identical.

HTTP requests involve remove methods, so the function might resemble

delete(client_state) = send_delete(client_state) -> receive_delete(client_state) 
                                                 -> respond_to_delete(informative_state) 
                                                 -> handle_response(informative_state) 
                                                 -> client_state - {item} 
Acolyte answered 22/10, 2012 at 18:58 Comment(3)
This is an interesting argument. I'm still not clear on how "OK" solves this inconsistency or forced ambiguity. I agree that if you provide an identical response, you're better off, but I don't see us breaking any state transitions if we choose to disambiguate "this resource never existed in the first place, so you can't delete it" (404) vs. "this resource used to be here, but it's not any more, so you should probably flush your cache" (406)...Lotetgaronne
... You'd get identical replies from the server in every situation where this was true for a specific resource during its lifetime. Asking for identicality through OK is asking the server to hide details about what has occurred before/after a client responds which may not be good design depending on the API. I'll chew on it more, but I still like DELETE 1 -> OK, DELETE 2..n - GONE for a resource that was ever created and DELETE 1..n 404 NOT FOUND for everything else.Lotetgaronne
@DanielCrenna but attempt to delete something means that you think that it exists (i.e. the client assumes that it must be on the server). For resources never created 404 is more proper code for GET operation.Stirrup
D
0

Wikipedia defines Idempotence as an operation that:

can be applied multiple times without changing the result beyond the initial application.

Notice that they talk about the result of the operation. To me, this includes both the server state and the response code.

The HTTP specification is a bit more vague on the matter. It defines it specifies that HTTP methods are Idempotent:

if the intended effect of multiple identical requests is the same as for a single request.

If you interpret effect as result in the Wikipedia definition then they mean the same. In any case, I question the practical benefit of telling clients that the resource as already been deleted.

Final point: Idempotence is defined in terms of a single client. Once you start introducing concurrent requests by other clients, all bets are off. You are supposed to use conditional-update headers (such as If-Match-ETag) to deal with such cases.

To reiterate: you should return the same return code, whether the resource just got deleted, was deleted by a previous request, or never existed at all.

Dulciedulcify answered 15/10, 2013 at 7:14 Comment(3)
No, it "effect" is not intended to cover the response as well. What you propose is that DELETE either always must pass ("200") or fail ("404" or "410"), no matter what the state of the server is. This doesn't make any sense and furthermore doesn't help the client at all.Tryptophan
@JulianReschke, if you read tools.ietf.org/html/… the specification lists all legal responses to DELETE. 404 and 410 are not one of them. As such, please remove the downvote.Dulciedulcify
no, the specification does not list "all" legal responses to DELETE. It just gives examples.Tryptophan

© 2022 - 2024 — McMap. All rights reserved.