[rabbitmq-discuss] Exactly Once Delivery

Tim Fox tim.fox at jboss.com
Mon Aug 9 11:49:42 BST 2010

On 07/08/10 12:50, Matthew Sackman wrote:
> On Fri, Aug 06, 2010 at 10:43:56PM +0100, Tim Fox wrote:
>> The way we do it in HornetQ is we have a well defined header key
>> "_HQ_DUP_ID". The client can set this with some unique value of it's
>> choice before sending (e.g. a GUID). When the server receives the
>> message if the _HQ_DUP_ID header is set, it looks up the value in
>> it's cache, and if it's seen it before it ignores it. The cache can
>> optionally be persisted.
> How do you prevent the cache from growing without bound?
Currently we use a circular buffer. It's up to the user to make it big 
enough for their use case. For JMS, where persistent sends/ tx commit 
must be synchronous, the buffer size would need to be somewhat larger 
than the max number of producers, since there's never more than one 
"inflight" message at any time per producer. But there's certainly an 
element of guesswork here.

In HornetQ we also provide an interface above and beyond JMS, which 
allows the user to receive an *asynchronous* ack that the message they 
sent (or tx commit) has been received ok on the server so they can clear 
it from their local resend cache. Since this is async it's not limited 
by network latency as in the blocking JMS case. The downside is many 
messages can be in-flight at any time per producer so the caches need to 
be larger.

To do all of this without being limited by arbitrary cache size, would 
need some kind of "ack of ack" (we don't implement this yet)- i.e. 1) 
the client sends message to server, 2) server sends ack back to to 
client to say "received-ok" 3) client sends further ack from client to 
server saying received-received-ok-ok. At point 2) the client can clear 
their resend cache. At point 3) the server can clear it's cache. I 
believe AMQP 1.0 specifies something similar to this too (?)

So.. this could scale. You'd have a further buffer per producer on the 
server side. If you're using TCP on the server, then every connection 
will have it's own buffer anyway. The extra buffer per producer should 
be of the same order of size as the TCP buffer, since it's effectively 
defined by a window, kind of similar to the TCP window size.

Like others have said, 100% once and only once delivery doesn't happen. 
To get very near at 100% you can implement stuff like the above, and 
also make sure your storage is highly redundant, also mirrored in 
geographically distributed sites in case the building blows up. Then 
you've got bugs in your own app, device drivers or the operating system 
that screw your once and only once (for example last week I hit a bug in 
the Linux kernel TCP impl which can cause packets to be lost at high 
load) that you might hit well before having to take quantum effects into 
account :)
> Matthew
> _______________________________________________
> rabbitmq-discuss mailing list
> rabbitmq-discuss at lists.rabbitmq.com
> https://lists.rabbitmq.com/cgi-bin/mailman/listinfo/rabbitmq-discuss

Sent from my BBC Micro Model B

Tim Fox

HornetQ - putting the buzz in messaging http://hornetq.org
fox at redhat.com

More information about the rabbitmq-discuss mailing list