qpid-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Oleksandr Rudyy <oru...@gmail.com>
Subject Re: Failover
Date Thu, 22 Sep 2011 16:19:05 GMT
Thanks Rajith for your commentaries.
I have discussed them with Robbie, our comments inline.

>> Qpid Java Client Failover Policy
>>
>> 1. Qpid client failover basic principles.
>> ===================================================
>>
>> When connection to broker is lost a Qpid client should be able to
>> re-establish connection to broker if failover policy is not switched
>> off by specifying "nofailover" as a failover option in a connection
>> URL.
>
>> The failover functionality on Qpid client should be based on
>> principle "stop the world".  When connection is lost and failover
>> starts the Qpid Client should not allow an invocation of any JMS
>> operation which requires sending or receiving data over the network.
>> Such operations should be blocked until failover functionality
>> restores the connectivity by using any of the supported failover
>> methods ('singlebroker', 'roundrobin', 'failover_exchange').
>
> I also think it would be useful if we could provide a brief write up
> about the different failover methods we supports and exactly what can
> be expected.
> We also need to clearly clarify things like reconnect_timeout,
> reconnect_limit and reconnect_interval etc and should align ourselves
> with the C++ and Python (Ruby...etc)  in terms of behaviour of these
> options.
>

How particular values like reconnect interval etc are attained/used
seems like it can be addressed separately from the messaging behaviour
of the client during failover, and so could later be addressed
separately.

>> On restoring connectivity blocked JMS operations should be allowed
>> to finish. If the failover functionality cannot re-establish the
>> connection a JMSException should be thrown within any JMS
>> operation requiring transferring data over the network.
>
> We need to come up with a clear strategy of notifying exceptions, an
> issue where we are falling short in the current implementation.
> If a connection listener is used then we have to notify via the
> connection listener as per the spec.
>
> Then comes the question of what are we going to do with a JMS
> operation that transfers data over the network.
> Are we still going to throw an exception as mentioned above? or are
> we going to throw an exception IFF there is no exception listener ?
>

Looking at the JMS spec section for ExceptionListener, it is quite
clear about intended behaviour in that scenario:

"A JMS provider should attempt to resolve connection problems itself prior to
notifying the client of them.

The exceptions delivered to ExceptionListener are those which don’t have any
other place to be reported. If an exception is thrown on a JMS call it, by
definition, must not be delivered to an ExceptionListener (i.e.
ExceptionListener is
not for the purpose of monitoring all exceptions thrown by a connection)."

> Next up, If we are throwing a JMS exception, how will the application
> know the difference between a session exception and a connection
> exception ?
> Ex.  Resource limit exceeded vs Connection issue ?
>

We would have to decide on either a hierarchy of exceptions, or
perhaps an error-code based solution, or both, in order to convey any
meaning in such situations.

> In the current code, this is an area which is causing deadlocks and
> race conditions. This also an area that is causing confusion among
> our users as they are not sure how exactly the client is going to
> behave.Therefore IMO I believe it's imperative to fix our exception
> handling if we are to provide our users with deterministic failover
> experience.

Agreed, see later for more discussion.

>> On successful failover, it is expected that client JMS session should
>> restore all temporary queues(topics) if such were created before
>> failover.
>
> This is not always the case and depends on what reliability gauntness
> are specified on a per destination basis via the "reliability" option.
> If "unreliable" or "at-most-once" is used, then I believe then the
> above is fine.
>
> If "at-least once" is used then we cannot simply create a new
> temporary queue, as btw the time the client failed over and
> reconnected, there may have been messages sent to the previous
> temp queue.
> If we simply create a new temp queue then those messages on the
> old queue will be lost.
> This is certainly the case with the C++ broker which supports
> clustering and with both brokers in the event of a temporary network
> failover.
>

By restore, we meant what the client currently appears to already do
with TemporaryQueues, create exclusive auto-delete queues of the same
name.

Since TemporaryQueues are created by the
Session.createTemporaryQueue() call there doesnt appear to be any
option to specify syntax such as link-options. It also doesn't seem
clear that we could guarantee at-least-once delivery for a temporary
queue which is deleted automatically when the underlying
Session/Connection is closed anyway, so perhaps a reasonable stance to
take would be that at-least-once on TemporaryQueues is not supported
across Failover?

>> 2. Description of failover behavior for JMS operations
>> ==================================================
>>
>> Session.commit() rollbacks transaction and throws a
>> TransactionRolledBackException if the session is dirty (there were
>> messages sent/received in the transaction) when failover occurred,
>> allowing the user to replay their transaction on the new Session.
>
> We need more clarification here.
> 1. Are we going to allow the same transacted session to be used just
> like the way we allow non transacted JMS sessions? In other words
> are we going to allow the JMS Session to resume once the exception
> is handled?
> 2. Or do we ask the application to create a new JMS session before
> continuing with the work ?
>
> Currently on 0-10 path we do #2, but there are customer requests to support #1.


#1 is what we meant, and seems like the only reasonable thing to do in
this situation.

>
>> Session.recover() and Message#acknowledge() should throw a
>> JMSException if there has been any message delivery since the last
>> acknowledgment was sent and failover occurred whilst the session
>> was 'dirty'
>
> Perhaps here we may have to make a distinction between, 1. Messages
> that were in the prefetch buffer but not yet delivered to the
> application 2. Messages that were delivered to the application but not
> yet acked (unacked message buffer).
>
> AFAIK we currently mark both categories of messages are redelivered.
> There has been some push back on this, asking us to mark only the
> delivered but unacked messages as "redelivered".
>

By delivered we meant messages handed to the application and not any
prefetched messages, which would not be considered to make the session
dirty.

After further consideration of recover(), it seems like it is not
necessarily possible to meet the contract for the method across
failover, even with a cluster. More later when discussing Client Ack.

>> Message consumer:
>>
>> No further messages sent to the client by the old broker should be
>> received by the consumer after failover has occurred, only messages
>> sent by the new broker should be available to consumers.
>
> Agreed, but pls see the above comment.

In this case, prefetched messages were included in the 'sent to the
client' group since we cant make use of those because we wont be able
to acknowledge them, and the broker may have also sent us those
messages again (if they still exist after we reconnect).

>
>>
>> Queue browser:
>>
>> If failover occurred while iterating through QueueBrowser
>> enumerations a sub-class of NotSuchElementException should be
>> thrown by default.
> Agreed.
>
>>
>>
>> 3. Issues with acknowledgments
>> ==================================================
>> 1. CLIENT_ACKNOWLEDGE
>>
>> The acknowledge operation should not return till all messages has
>> actually been acknowledged. Currently is possible for messages not
>> being acknowledged after invoking acknowledge operation. The
>> acknowledge is done lazily by acknowledgment flusher, however,
>> this is not what the JMS spec requires.
>
> I believe we need to handle 3 cases during failover.
> I have provided a detailed analysis of the above in QPID-3462
>

For the 1st of your cases, the acknowledement can only be in doubt if
the acknowledge method fails; if the method returns there should be no
doubt. As you concluded, this should always be a synchronous
operation, which currently it isnt since half the time it doesnt even
send an acknowledgement at all.

For the second case, the acknowledge call should throw an exception as
you indicated. Although it is possible, it isnt guaranteed the next
message delivered will be the same message since it could have been
given to another client whilst reconnecting during failover, could
have been transient and lost on a restart, or in case non-clustered
brokers just may not be there after failover. The next message after
failover would just be the first message provided by the new broker to
the client.

In the third case we somewhat disagree based on the example. After
failover occurs, we cant simply give the application the previous
message again. As mentioned above, this may not be a message that is
available any more, and since we know we cant acknowledge messages
received from prior to failover anyway it doesnt make sense to give it
to the application a second time and then throw an exception upon
acknowledgement. Perhaps the example is assuming the broker will
always send the same message again first? Also, it isnt clear what
behaviour this is implying if there were e.g. 3+ messages received in
the same acknowledge window.

If the session is dirty during failover in Client Ack mode, we need to
simply drop knowledge of the previous messages given to the client by
the broker (prefetched or delivered to the app) but retain knowledge
of whether the session was dirty during failover. We also have to
accept that as not all the messages may still be available to the
client after failover then we cant guarantee to meet the contract for
recover() across failover either (looking at the code, it looks like
we currently cant meet it regardless of failover). The question for us
then is really how we convey that to the application, possibly
depending on what it does next. We could use the exception listener
[if there is one] at the point the failover occurs to indicate
failover happened, or we could wait and throw an exception on
acknowledge() / recover() [whichever is called, since only one can be
on the same dirty session before it is then clean again] to indicate
we cant meet the expected behaviour due to failover, or we could throw
an exception on receive() if we knew such calls couldnt deliver
messages that could cleanly be acknoweldged due to failing over with
previously unacked messages (but the client may only be using
onMessage and so not get such exceptions). Alternatively, you could
argue that recover() should never throw an exception since it cant
never be guaranteed to work, and so that could just be documented
behaviour without an exception. The sticking point with all of the
above is what would be considered most appropriate/helpful from the
client application perspective.

Related to this, when using transactions similar questions arise
around rollback(), commit(), send() and receive(). If the session is
dirty at the point failover occurs, it is known that the first
subsequent commit() should cause a rollback followed by
TransactionRolledBackException. However, it is also known that any
send() or receive() calls before such a commit() are irrelevant as
that work will rolled back, so should we throw an exception at this
point to indicate that. Currently the 0-9 codepath does this on
send(), but the 0-10 client doenst and neither do it on receive(). For
rollback, it can be argued it should never throw an exception due to
what it is doing, although applications calling rollback might expect
the same messages to be received and that may not be the case due to
the failover so it could instead be argued that like recover() an
exception should be raised.


>> 2.AUTO_ACKNOWLEDGE
>>
>> The JMS requires that after each call to receive operation or
>> completion of each onMessage the received message should be
>> acknowledged. Currently this does not happens as the acknowledge is
>> done lazily by acknowledgment flusher which does not give the same
>> guarantee. This is in fact is DUPS_OK_ACKNOWLEDGE behavior. The
>> flusher thread should not be running for AUTO_ACKNOWLEDGE.
>
> In order to be spec compliant applications need to use -Dsync_ack=true
> which will force the client to sync with the broker after each
> onMessage or receive call.
> However this is terribly slow, hence the reason why it behaves like DUPS_OK.
>
> I don't necessarily condone this behaviour (neither did I code this
> part),  but making it spec compliant might suddenly make a lot of
> applications crawl :D So we need to be very careful here.  If we do we
> need to make a lot of noise about this in release notes etc...
>
> The ack-flusher was done to periodically flush message completions
> during periods of relatively slow message flows.
> If we fix AUTO_ACK then I agree this does not need to run during AUTO_ACK.
>

In the 0-8 client, it sends acks asynchronously but at least does it
on a per-message basis rather than batching them up for X period of
time. We could perhaps investigate that behaviour for the 0-10 client
if not actually making full on synchronous acking the default.

>> 3. SESSION_TRANSACTED
>> Flusher thread is started but it is not really needed because
>> acknowledgment for transacted sessions is handled differently.
>> It seems that a flusher thread make sense to run in a
>> DUPS_OK_ACKNOWLEDGE modes only.
>
> This is actually a bug. As you say we only need to run it during
> DUPS_OK and AUTO_ACK (until auto-ack gets fixed).
>

(Some commentary about transacted sessions was given above in the
client ack section along with related comments)

>>
>> 4. NO_ACKNOWLEDGE
>>
>> The flusher thread should not be running for NO_ACKNOWLEDGE.
>
> We need to clearly define how the JMS ack modes will work along
> side the "reliability" option in address strings.

Agreed. In requesting NO_ACK mode it seems fairly clear you didnt want
to ack messages by using this custom acknowledge mode, so if that
conflicts badly with any selected link options such as at-least-once
it seems it would warrant an exception being raised (i.e. the JMS
Acknowledge Mode selected in the code takes precedence).

>
>>
>> 5. PRE_ACKNOWLEDGE
>>
>> The same issue as with AUTO_ACKNOWLEDGE.
>> Is anybody using this mode? Does it make sense to keep it?
>
> I don't know if anybody is using it. I wonder whats the use case behind
> this.

I'm not sure anyone does. It acknowledges before onMessage() rather
than after, which in the case of the 0-10 codepath makes little
difference by default since auto-ack, dups-ok, and pre-ack all
currently depend on the flusher thread to do the acknowledements.





As a little aside, whilst investigating the various options for
Failover, I found the following document describing Oracle Weblogic
failover policy at
http://download.oracle.com/docs/cd/E13222_01/wls/docs92/jms/recover.html
which seems useful to consider. Investigating what some other vendors
do with their JMS client recovery seems like it could be useful.


Also, I created a number of tests to cover failover functionality but
I can not commit them till failover behaviour is finalized.

Kind Regards,
Alex

---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:dev-subscribe@qpid.apache.org


Mime
View raw message