-
1. Re: Acknowledgment handling in the core
ovidiu.feodorov May 17, 2005 5:20 PM (in response to ovidiu.feodorov)The problem described by Tim is indirectly related to the way a Router handles acknowlegments. Actually, there are two distinct problems that need to be solved
1. A channel cannot distinguish between messages that have been delivered and NACKed and messages that haven't been delivered at all. We need this distinction to implement channel browsers.
2. Because a Router is currently a Receiver, its handle() method can only return a binary result (ACK/NACK) which is certainly not sufficient to express combinations of ACKs/NACKs from multiple receivers, in the case of a PointToMultipointRouter, for example.
Tim proposed to solve issue 1 by having three acknowledgment types . This would require changes throughout the core. I also think three acknowledgment types are not necessary, since NACK-DeliveryNotAttempted is a state that shouldn't propagate back on the receiver chain. We don't need to be able to browse all channels along a delivery path, we only need to be able to browse the last one, that actually keeps the message in question.
Here is another proposal that addresses 1. and 2. and it only requires localized changes. We still only have two types of acknowledgments: ACKs (true) and NACKs (false).
As originally designed, a Router is a synchronous component. It returns a NACK to the component that initiated delivery, if the delivery is not successful, but it does not hold messages and it does not attempt redelivery. In this respect, is not a Channel. To address 1. and 2. its interface should changes as followspublic interface Router extends Distributor { public Serializable getRouterID(); public Serializable[] handle(Routable); public Serializable[] handle(Routable, Serializable[]); public boolean isPassByReference(); }
A Router is not a Receiver anymore, since the Receiver interface is not semantically rich enough.
The handle() method behaves as follows: the sender attempts delivery using handle(Routable) or handle(Routable, null). The Router synchronously delivers the Routable to the receivers it chooses (all receivers for a PointToMultipointRouter or only one receiver for a PointToPointRouter) and returns the array containing ReceiverIDs of receivers that NACKed. This way, the sender can store the NACKs and attempt redelivery later using handle(Routable, Serializable[]).
If the Router has no receivers, handle() only returns a single element array containing the Router's ID. This way, the sender knows the message was not delivered in the first place (this is the equivalent to Tim's NACK-DeliveryNotAttempted). The pipe that calls into the Router stores the message and it's also able to distinguish between NACKed messages and non-delivered messages.
Browsing the queue would mean quering the AcknowledgmentStore for all messages that have been NACKed by the Router and not by individual Receivers. -
2. Re: Acknowledgment handling in the core
adrian.brock May 17, 2005 5:24 PM (in response to ovidiu.feodorov)Tim wrote:
Further to the discussion regarding asynchronous ACKs, here are some thoughts on how we can implement QueueBrowsing on the core classes and how it all works together.
The assumption here is that we want to be able to browse any messages in a queue where delivery has not been attempted. We don't want to browse those messages where the message has been delivered but the JMS client has NACKed it.
That assumption is NOT correct. If the message has been NACKed it is in the queue
again and can be browsed just like any other message in the queue. -
3. Re: Acknowledgment handling in the core
timfox May 17, 2005 5:43 PM (in response to ovidiu.feodorov)
That assumption is NOT correct. If the message has been NACKed it is in the queue
again and can be browsed just like any other message in the queue.
That's good... So we just browse any NACK. That should make thngs considerably simpler.
We've come full circle back to the original design approach I posted but along the way I've learnt quite a lot about core, so that's good :) -
4. Re: Acknowledgment handling in the core
ovidiu.feodorov May 17, 2005 6:24 PM (in response to ovidiu.feodorov)
That assumption is NOT correct. If the message has been NACKed it is in the queue again and can be browsed just like any other message in the queue.
JMS Specification is very vague about this:
Section 5.9
A client uses a QueueBrowser to look at messages on a queue without removing them. A QueueBrowser can be created from a Session or a QueueSession.
The browse methods return a java.util.Enumeration that is used to scan the queue?s messages. It may be an enumeration of the entire content of a queue, or it may contain only the messages matching a message selector.
Messages may be arriving and expiring while the scan is done. JMS does not require the content of an enumeration to be a static snapshot of queue content. Whether these changes are visible or not depends on the JMS provider.
Nowhere in this section is explicitely specified whether the "entire content of the queue" consists in messages that have not been delivered AND messages that have been delivered and NACKed, or only messages that haven't been delivered.
JBossMQ's QueueBrowsers do not return NACKed messages, only undelivered messages.
At this point, it is not very clear to me what a browser should return, actually. We could have this as a configuration option, I believe. -
5. Re: Acknowledgment handling in the core
adrian.brock May 17, 2005 6:46 PM (in response to ovidiu.feodorov)All that is saying is that the jms implementation is NOT required to give
a synchronized snapshot and that it is not transactional.
In other words, unless you have no concurrent producers/consumers
(and no message expiration/scheduled delivery) the
messages returned by the browser are entirely unpredictable.
e.g.Enumeration e = browser.getEnumeration(); Message m1 = receiver.receive(); print(e);
Will it show m1 in the enumeration? Who knows? It is implementation defined. -
6. Re: Acknowledgment handling in the core
ovidiu.feodorov May 17, 2005 8:05 PM (in response to ovidiu.feodorov)Right.
And that's why JBossMQ's browsers do not show messages currently in queue but being in process of being acknowledged, while Reference Implementation's browsers do.
Tim, what about a Channel.browse() method? Your QueueBrowser will rely on Channel's behavior. Later we can even make this behavior configurable in the Queue's deployment descriptor. -
7. Re: Acknowledgment handling in the core
timfox May 18, 2005 3:19 AM (in response to ovidiu.feodorov)We _could_ do this either way and make it configurable by the user.
However, discriminating the two types of NACKs adds significant complexity to the implementation (it prompted this whole discussion) including changes to the core interfaces.
My gut feeling is to keep it simple unless we really think the new feature would add value for the user.
I.e. perhaps we should ask the quesion: Would a user really care about discriminating those messages that have never been delivered and those that have been NACKed by the channel's receivers?
I'm sure there probably is a use case where this distinction is important - but I can't think of one right now.
My feeling is that in most cases JMS users just want to have a rough idea of what's on the queue and hence what's going to be (re)delivered when the consumer/durables subcriber attaches.
(I add durable subscriber here - since even though it's not part of JMS spec, browsing durable subscribers is something we could add, that does add value IMO... but that's a different discussion).
Moreover, as Adrian has pointed out, the JMS spec makes no guarantees as to the consistency/"staticness" so anyone building a JMS application that relies on some extra knowledge of what's in the queue other than what's guaranteed by the spec. would be non-portable between JMS implementations.
If it really is important for JMS Users to distinguish messages that have been never delivered/messages that have been delivered once etc., then another approach could be to use a jmsx message property, e.g. delivery_count which gets incremented every-time a delivery is attempted.
The JMS user could then just filter on this value via a message selector when they do the browse.
This should allow them to discriminate between these messages, and would require no changes to the core class interfaces.
I think this approach is used by several jms implementations (i'm not sure about this but I just googled) including websphere and activemq. -
8. Re: Acknowledgment handling in the core
genman May 18, 2005 8:58 AM (in response to ovidiu.feodorov)
There's a number of JBoss specific properties to the 3.2/4.0, one of which includes the number of delivery attempts. (This is used primarily by the MDB code, so that when the number of attempts reaches 10 the message goes to the DLQ.) -
9. Re: Acknowledgment handling in the core
adrian.brock May 18, 2005 11:18 AM (in response to ovidiu.feodorov)"timfox" wrote:
I.e. perhaps we should ask the quesion: Would a user really care about discriminating those messages that have never been delivered and those that have been NACKed by the channel's receivers?
I'm sure there probably is a use case where this distinction is important - but I can't think of one right now.
Besides genman's comment there is the spec defined:
javax.jms.Message.getJMSRedelivered()