[ 
https://issues.apache.org/activemq/browse/AMQ-2552?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Stirling Chow updated AMQ-2552:
-------------------------------

    Description: 
Symptom
========
If a queue is loaded with 400+ messages that never expire, and then a message 
that does expire is added to the end of the queue, the added message will not 
be processed by the Broker's expiration logic until some of the 400+ unexpired 
messages are consumed.  A unit test is provided to demonstrate this issue.

These test cases worked for AMQ 5.3, but not for AMQ 5.3.1-SNAPSHOT r893661.

Motivation
========
Our application uses message expiration to trigger actions when messages are 
left in a queue for too long a period.  In many cases, there are no consumers 
on the queue containing the messages that might expire.  Prior versions of AMQ 
only expired messages when an attempt was made to dispatch them to a consumer, 
but AMQ-1112 introduced the notion of periodic, automatic expiration of pending 
messages (see the expireMessagesPeriod policy).  With this periodic expiration, 
our expectation is that expired messages will be detected and processed in a 
timely fashion, regardless of the presence of consumers on the queue.

Cause
======
AMQ-2481 removed forced paging from the periodically-called expireMessages() 
method:

>>
        doBrowse(true, browsedMessages, this.getMaxExpirePageSize());
<<
        doBrowse(browsedMessages, this.getMaxExpirePageSize());

The purpose of the change was to limit the number of messages that would be 
paged into memory from the message store.  The side effect is that only the 
first 400 messages in the queue are ever inspected by expireMessages().  If 
these 400 messages do not contain the messages that are expiring and there are 
no active consumers to move the expiring messages forward, then expiration will 
never occur.  With AMQ 5.3, successive calls to expireMessages() would 
eventually pull all messages into the page so they could be checked for 
expiration.

It should also be noted that the test cases can be modified so there is a 
consumer (with a selector that doesn't choose any of the messages), and they 
still fail showing that the issue is not purely the result of having *no* 
consumers.

Resolution
=========
I don't think the browse idiom works very well for the automated expiration 
task.  It was my initial assumption (and expectation) that the 
expireMessagesPeriod task would process *all* expired messages in the queue 
*each time* the task fired.  A related issue to limiting the number of messages 
that are examined during each expiration task is that expiring messages at the 
end of the queue will not be handled in a timely fashion since they must wait 
for multiple firings of the expiration task (this is on 5.3 code; 5.3.1 code 
will never get to those messages).

If the desire/need is to scan all messages to determine which have expired, the 
existing paging strategy is limited because it would require all messages to be 
brought from store into memory, thus defeating the whole point of paging.  An 
alternative might be to have a sliding page window that would allow batches of 
messages to be brought in while clearing out the previous batch.

Another solution might be to have the expiration task page in directly from the 
store, but this is problematic since non-persistent messages would still have 
to come in from the pending message cursor.  In addition, a complete scan of 
the store might be non-performant for large queues.

I think the most satisfactory solution for minimizing unnecessary processing 
and providing timely expiration of messages is to implement a proper heap that 
indexes messages with expiration values and allows immediate access to *only* 
the messages that have expired.  The references to these expired messages could 
then be used to remove the messages directly from the cursors and the store 
rather than having to iterate over these structures.


  was:
Symptom
========
If a queue is loaded with 400+ messages that never expire, and then a message 
that does expire is added to the end of the queue, the added message will not 
be processed by the Broker's expiration logic until some of the 400+ unexpired 
messages are consumed.  A unit test is provided to demonstrate this issue.

These test cases worked for AMQ 5.3, but not for AMQ 5.3.1-SNAPSHOT r893661.

Motivation
========
Our application uses message expiration to trigger actions when messages are 
left in a queue for too long a period.  In many cases, there are no consumers 
on the queue containing the messages that might expire.  Prior versions of AMQ 
only expired messages when an attempt was made to dispatch them to a consumer, 
but AMQ-1112 introduced the notion of periodic, automatic expiration of pending 
messages (see the expireMessagesPeriod policy).  With this periodic expiration, 
our expectation is that expired messages will be detected and processed in a 
timely fashion, regardless of the presence of consumers on the queue.

Cause
======
AMQ-2481 removed forced paging from the periodically-called expireMessages() 
method:

>>
        doBrowse(true, browsedMessages, this.getMaxExpirePageSize());
<<
        doBrowse(browsedMessages, this.getMaxExpirePageSize());

The purpose of the change was to limit the number of messages that would be 
paged into memory from the message store.  The side effect is that only the 
first 400 messages in the queue are ever inspected by expireMessages().  If 
these 400 messages do not contain the messages that are expiring and there are 
no active consumers to move the expiring messages forward, then expiration will 
never occur.  With AMQ 5.3, successive calls to expireMessages() would 
eventually pull all messages into the page so they could be checked for 
expiration.

It should also be noted that the test cases can be modified so there is a 
consumer (with a selector that doesn't choose any of the messages), and they 
still fail showing that the issue is not purely the result of having *no* 
consumers.

Resolution
=========
I don't think the browse idiom works very well for the automated expiration 
task.  It was my initial assumption (and expectation) that the 
expireMessagesPeriod task would process *all* expired messages in the queue 
*each time* the task fired.  A related issue to limiting the number of messages 
that are examined during each expiration task is that expiring messages at the 
end of the queue will not be handled in a timely fashion since they must wait 
for multiple firings of the expiration task (this is on 5.3 code; 5.3.1 code 
will never get to those messages).

If the desire/need is to scan all messages to determine which have expired, the 
existing paging strategy is limited because it would require all messages to be 
brought from store into memory, thus defeating the whole point of paging.  An 
alternative might be to have a sliding page window that would allow batches of 
messages to be brought in while clearing out the previous batch.

Another solution might be to have the expiration task page in directly from the 
store, but this is problematic since non-persistent messages would still have 
to come in from the pending message cursor.  In addition, a complete scan of 
the store might be non-performant for large queues.

I think the most satisfactory solution for minimizing unecessary processing and 
providing timely expiration of messages is to implement a proper heap that 
indexes messages with expiration values and allows immediate access to *only* 
the messages that have expired.  The references to these expired messages could 
then be used to remove the messages from the cursors and the store.



> Automatic message expiration may not occur on a queue with no (or slow) 
> consumers.
> ----------------------------------------------------------------------------------
>
>                 Key: AMQ-2552
>                 URL: https://issues.apache.org/activemq/browse/AMQ-2552
>             Project: ActiveMQ
>          Issue Type: Bug
>          Components: Broker
>    Affects Versions: 5.3.1
>         Environment: ActiveMQ 5.3.1 Snapshot r893661
>            Reporter: Stirling Chow
>         Attachments: AMQTest.java
>
>
> Symptom
> ========
> If a queue is loaded with 400+ messages that never expire, and then a message 
> that does expire is added to the end of the queue, the added message will not 
> be processed by the Broker's expiration logic until some of the 400+ 
> unexpired messages are consumed.  A unit test is provided to demonstrate this 
> issue.
> These test cases worked for AMQ 5.3, but not for AMQ 5.3.1-SNAPSHOT r893661.
> Motivation
> ========
> Our application uses message expiration to trigger actions when messages are 
> left in a queue for too long a period.  In many cases, there are no consumers 
> on the queue containing the messages that might expire.  Prior versions of 
> AMQ only expired messages when an attempt was made to dispatch them to a 
> consumer, but AMQ-1112 introduced the notion of periodic, automatic 
> expiration of pending messages (see the expireMessagesPeriod policy).  With 
> this periodic expiration, our expectation is that expired messages will be 
> detected and processed in a timely fashion, regardless of the presence of 
> consumers on the queue.
> Cause
> ======
> AMQ-2481 removed forced paging from the periodically-called expireMessages() 
> method:
> >>
>         doBrowse(true, browsedMessages, this.getMaxExpirePageSize());
> <<
>         doBrowse(browsedMessages, this.getMaxExpirePageSize());
> The purpose of the change was to limit the number of messages that would be 
> paged into memory from the message store.  The side effect is that only the 
> first 400 messages in the queue are ever inspected by expireMessages().  If 
> these 400 messages do not contain the messages that are expiring and there 
> are no active consumers to move the expiring messages forward, then 
> expiration will never occur.  With AMQ 5.3, successive calls to 
> expireMessages() would eventually pull all messages into the page so they 
> could be checked for expiration.
> It should also be noted that the test cases can be modified so there is a 
> consumer (with a selector that doesn't choose any of the messages), and they 
> still fail showing that the issue is not purely the result of having *no* 
> consumers.
> Resolution
> =========
> I don't think the browse idiom works very well for the automated expiration 
> task.  It was my initial assumption (and expectation) that the 
> expireMessagesPeriod task would process *all* expired messages in the queue 
> *each time* the task fired.  A related issue to limiting the number of 
> messages that are examined during each expiration task is that expiring 
> messages at the end of the queue will not be handled in a timely fashion 
> since they must wait for multiple firings of the expiration task (this is on 
> 5.3 code; 5.3.1 code will never get to those messages).
> If the desire/need is to scan all messages to determine which have expired, 
> the existing paging strategy is limited because it would require all messages 
> to be brought from store into memory, thus defeating the whole point of 
> paging.  An alternative might be to have a sliding page window that would 
> allow batches of messages to be brought in while clearing out the previous 
> batch.
> Another solution might be to have the expiration task page in directly from 
> the store, but this is problematic since non-persistent messages would still 
> have to come in from the pending message cursor.  In addition, a complete 
> scan of the store might be non-performant for large queues.
> I think the most satisfactory solution for minimizing unnecessary processing 
> and providing timely expiration of messages is to implement a proper heap 
> that indexes messages with expiration values and allows immediate access to 
> *only* the messages that have expired.  The references to these expired 
> messages could then be used to remove the messages directly from the cursors 
> and the store rather than having to iterate over these structures.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to