I wanted to add acknowledgement to the queue consumer pull model in REST-* Messaging. The way it would work is that consumers do a POST on the queue’s URL. They receive the message as well as a Link header pointing to an acknowledgement resource. When the client consumer successfully processes the message, it posts a form parameter, acknowledge=true to the acknowledgement link.
There is a problem with this though. The design is connectionless to honor the stateless REST principle. So there is no specific session resource that the client consumer is interacting with. The consumer may never acknowledge the message, so I need the server to re-enqueue the message and deliver it to a new consumer. The problem is, what if the old consumer tries to acknowledge after the message is re-enqueued or even after it is redelivered to a different consumer?
I first thought of letting the first consumer to acknowledge win and do something like POST-Once-Exactly (POE). The problem with this is, what if there’s a network failure and the consumer doesn’t know if the acknowledgement happened or not? It would redeliver the message and get back a Method Not Allowed response error code. With this code, the consumer doesn’t know if somebody else acknowledged the message or if the older request just went through. So, I went with a conditional POST. The acknowledgement link, when performing a GET on it, would return an ETag header that the consumer must transmit with the acknowledgement POST. If the message was re-enqueued, then the underlying ETag would change, and the conditional post would fail for the older consumer.
Still this solution is suboptimal because an additional GET request needs to be executed. It is also subject to a race condition. What if the message is re-enqueued before the consumer does a GET on the acknowledgement resource? SO, what I decided to do was embed the etag value with the acknowledgement link. For example:
1. Consume a message
Request:
POST /myqueue/consumer
Response:
HTTP/1.1 200 OK Link: </myqueue/messages/111/acknowledgement>; rel=acknowledgement; etag=1 Content-Type: ... ... body ...
2. Acknowledge the message
Request:
POST /myqueue/messages/111/acknowledgement If-Match: 1 Content-Type: application/x-www-form-urlencoded acknowledge=true
Success Response:
HTTP/1.1 204 No Content
Response when it was updated by somebody else.
HTTP/1.1 412 Precondition Failed
POE Redelivery Response. It was already successfully updated by the consumer.
HTTP/1.1 405 Method Not Allowed
Nov 12, 2009 @ 20:16:52
The way I usually handle this is the embed the concurrency information in the URI:
<link rel="rel=acknowledgement" href="/myqueue/messages/111/acknowledgement;a1s2d3f4g5h6" /<
This has the effect of creating a unique URI for the action; one that can be treated as safe by the server. The server can decide if this URI can be replayed (i.e. “if you don’t hear from the server, it’s OK to activate this link again”) or is only good for a single use (i.e. “Sorry, that ack was already sent.”). In either case, the server can send any response code that is appropriate, including 412, 303, etc. depending on the state of things at the time.
The key advantage here is that clients do not need to have any special coding to handle the operation. They can use the link and let the server decide the outcome. It also does not do any overloading of the use or meaning of the existing ETag header.
Nov 12, 2009 @ 20:50:02
In my example, ETag is not overloaded, IMO. Its just the version id of the resource (the ack). I like the solution though. Thanks Mike.