Stirling Chow created AMQ-4151:
----------------------------------

             Summary: Duplicate non-persistent messages that are sent to a 
queue are either dispatched multiple times (i.e., not detected as duplicates) 
or cause the queue size to be miscalculated.
                 Key: AMQ-4151
                 URL: https://issues.apache.org/jira/browse/AMQ-4151
             Project: ActiveMQ
          Issue Type: Bug
            Reporter: Stirling Chow


Symptom
=======
We have a virtual topic that is shared by a network of brokers.  Each broker 
has consumers that process the corresponding Consumer.*.VirtualTopic.> queue.

While testing the effects of memory limits on our system, we encountered 
AMQ-4148, which resulted in duplicate subscriptions being made to the virtual 
topic.  These duplicate subcriptions were attempting to enqueue the same topic 
message multiple times to the corresponding Consumer.*.VirtualTopic.>.

Although logic exists in queues to handle duplicate messages, we noticed lots 
of strange behaviour with duplicate handling.  We decided to investigate 
further and found a race condition whereby duplicate non-persistent messages 
that were correctly ignored resulted in the queue size appearing to be 
non-empty when in fact there were no messages.

While our original investigation was prompted by AMQ-4148, the test case we 
attached to this ticket does not rely on the misbehaviour caused by AMQ-4148, 
but can occur during normal operation of network bridges when there are 
multiple consumers for a virtual topic (as is the case, e.g., when conduit 
subscriptions are disabled).

Cause
=====
If a virtual topic has multiple consumers, then sending a message to the topic 
results in multiple dispatches of the same message (one to each consumer).  If 
the multiple consumers originate from the same remote broker (e.g., because 
conduit subscriptions are disabled), then a single message sent to the virtual 
topic on broker1 will result in multiple duplicate messages being sent to the 
virtual topic on broker2.

If broker2 has corresponding Consumer.*.VirtualTopic.> queue, then the multiple 
duplicate messages sent to the virtual topic on broker2 will result in multiple 
duplicate messages being sent to the queue.  

If the messages are non-persistent, the only logic that prevents each duplicate 
message from being dispatched to a consumer is as follows:

{code:title=Queue.java}
private PendingList doPageInForDispatch(boolean force) throws Exception {
...
        // Only add new messages, not already pagedIn to avoid multiple
        // dispatch attempts
        pagedInMessagesLock.writeLock().lock();
        try {
            if(isPrioritizedMessages()) {
                resultList = new PrioritizedPendingList();
            } else {
                resultList = new OrderedPendingList();
            }
            for (QueueMessageReference ref : result) {
                if (!pagedInMessages.containsKey(ref.getMessageId())) {
                    pagedInMessages.put(ref.getMessageId(), ref);
                    resultList.addMessageLast(ref);
                } else {
                    ref.decrementReferenceCount();
                }
            }
        } finally {
            pagedInMessagesLock.writeLock().unlock();
        }
...        
{code}

If the consumers are fast and acknowledge the initial message dispatch before 
the next duplicate message is sent to the queue, then the 
{{pagedInMessages.constainsKey(...)}} check will *not* prevent the duplicate 
message from being dispatched since the message ID will have already been 
removed as part of the acknowledgement.

This is problem 1: duplicate detection fails if acknowledgements are quick

If the consumers are slow and don't acknowledge the initial message dispatch 
before the next duplicate message is sent to the queue, then duplicate 
detection will correctly ignore the message.  However, by this time, the 
duplicate message has already incremented the queue size:

{code:title=Queue.java}
final void messageSent(final ConnectionContext context, final Message msg) 
throws Exception {
    destinationStatistics.getEnqueues().increment();
    destinationStatistics.getMessages().increment();
    messageDelivered(context, msg);
{code}

The call to {{messageSent}} is made by the thread sending the message to the 
queue.  This thread is completely unaware that the message was ignored since 
the call to {{doPageInForDispatch}} is done by a separate thread (e.g., the 
taskrunner calling {{iterate()}}.

Since the queue size is incremented, but the duplicate message is never 
dispatched, there is no subsequent acknowledgement ot reduce the queue size.  
As a result, each duplicate message that is ignored remains counted in the 
queue size even though it's no longer in the queue.  

This is problem 2: if duplication detection succeeds, the queue size 
incorrectly counts the duplicate/ignored message.

Which problem gets exhibited depends on a race condition between the thread(s) 
enqueueing the duplicate virtual topic subscriptions and consumer threads 
acknowledging the dispatches.

While the unit test's methodology of creating multiple virtual topic consumers 
is somewhat contrived, there must be other circumstances under which duplicate 
messages are sent to queues (otherwise, why would there be logic to handle this 
case?)  In this context, the test case reveals a problem with the handling of 
duplicate messages.

Solution
========
None at this time.  We worked around the problem by patching AMQ-4148 to 
prevent the duplicate subscriptions that cause this bug.  However, our concern 
has been raised about AMQ's generally handling of duplicate messages sent to a 
queue.





--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to