All message dispatching should occur from the session's executor. Also, we should dispatch any messages in the consumers queue before dispatching messages in the session's queues.

http://issues.apache.org/activemq/browse/AMQ-1031
http://issues.apache.org/activemq/browse/AMQ-1032



git-svn-id: https://svn.apache.org/repos/asf/incubator/activemq/trunk@472345 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Hiram R. Chirino 2006-11-08 00:14:56 +00:00
parent c636b3798f
commit da5139c24b
8 changed files with 127 additions and 71 deletions

View File

@ -254,8 +254,6 @@
<!-- TODO need to get the JUnit test configured to create SSL sockets nicely via system properties --> <!-- TODO need to get the JUnit test configured to create SSL sockets nicely via system properties -->
<exclude>**/StompSslTest.*</exclude> <exclude>**/StompSslTest.*</exclude>
<!-- TODO reproduces a bad ack bug -->
<exclude>**/RollbacksWhileConsumingLargeQueueTest.*</exclude>
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>

View File

@ -776,6 +776,11 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
} }
if(deliveredMessages.isEmpty()) if(deliveredMessages.isEmpty())
return; return;
// Only increase the redlivery delay after the first redelivery..
if( rollbackCounter > 0 )
redeliveryDelay = redeliveryPolicy.getRedeliveryDelay(redeliveryDelay);
rollbackCounter++; rollbackCounter++;
if(rollbackCounter>redeliveryPolicy.getMaximumRedeliveries()){ if(rollbackCounter>redeliveryPolicy.getMaximumRedeliveries()){
// We need to NACK the messages so that they get sent to the // We need to NACK the messages so that they get sent to the
@ -791,8 +796,15 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
}else{ }else{
// stop the delivery of messages. // stop the delivery of messages.
unconsumedMessages.stop(); unconsumedMessages.stop();
for(Iterator iter=deliveredMessages.iterator();iter.hasNext();){
MessageDispatch md=(MessageDispatch) iter.next();
md.getMessage().onMessageRolledBack();
unconsumedMessages.enqueueFirst(md);
}
if( redeliveryDelay > 0 ) {
// Start up the delivery again a little later. // Start up the delivery again a little later.
redeliveryDelay = redeliveryPolicy.getRedeliveryDelay(redeliveryDelay);
Scheduler.executeAfterDelay(new Runnable(){ Scheduler.executeAfterDelay(new Runnable(){
public void run(){ public void run(){
try{ try{
@ -803,11 +815,10 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
} }
} }
},redeliveryDelay); },redeliveryDelay);
for(Iterator iter=deliveredMessages.iterator();iter.hasNext();){ } else {
MessageDispatch md=(MessageDispatch) iter.next(); start();
md.getMessage().onMessageRolledBack();
unconsumedMessages.enqueueFirst(md);
} }
} }
deliveredCounter-=deliveredMessages.size(); deliveredCounter-=deliveredMessages.size();
deliveredMessages.clear(); deliveredMessages.clear();
@ -820,6 +831,7 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
public void dispatch(MessageDispatch md) { public void dispatch(MessageDispatch md) {
MessageListener listener = this.messageListener; MessageListener listener = this.messageListener;
try { try {
synchronized(unconsumedMessages.getMutex()){
if (!unconsumedMessages.isClosed()) { if (!unconsumedMessages.isClosed()) {
if (listener != null && unconsumedMessages.isRunning() ) { if (listener != null && unconsumedMessages.isRunning() ) {
ActiveMQMessage message = createActiveMQMessage(md); ActiveMQMessage message = createActiveMQMessage(md);
@ -843,8 +855,9 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
} }
} }
} }
}
} catch (Exception e) { } catch (Exception e) {
log.warn("could not process message: " + md, e); session.connection.onAsyncException(e);
} }
} }
@ -853,18 +866,12 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
} }
public void start() throws JMSException { public void start() throws JMSException {
if (unconsumedMessages.isClosed()) {
return;
}
started.set(true); started.set(true);
unconsumedMessages.start(); unconsumedMessages.start();
MessageListener listener = this.messageListener; session.executor.wakeup();
if( listener!=null ) {
MessageDispatch md;
while( (md = unconsumedMessages.dequeueNoWait())!=null ) {
ActiveMQMessage message = createActiveMQMessage(md);
beforeMessageIsConsumed(md);
listener.onMessage(message);
afterMessageIsConsumed(md, false);
}
}
} }
public void stop() { public void stop() {
@ -876,4 +883,28 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
return "ActiveMQMessageConsumer { value=" +info.getConsumerId()+", started=" +started.get()+" }"; return "ActiveMQMessageConsumer { value=" +info.getConsumerId()+", started=" +started.get()+" }";
} }
/**
* Delivers a message to the message listener.
* @return
* @throws JMSException
*/
public boolean iterate() {
MessageListener listener = this.messageListener;
if( listener!=null ) {
MessageDispatch md = unconsumedMessages.dequeueNoWait();
if( md!=null ) {
try {
ActiveMQMessage message = createActiveMQMessage(md);
beforeMessageIsConsumed(md);
listener.onMessage(message);
afterMessageIsConsumed(md, false);
} catch (JMSException e) {
session.connection.onAsyncException(e);
}
return true;
}
}
return false;
}
} }

View File

@ -61,8 +61,8 @@ public class ActiveMQSessionExecutor implements Task {
} }
} }
private void wakeup() { public void wakeup() {
if( !dispatchedBySessionPool && hasUncomsumedMessages() ) { if( !dispatchedBySessionPool ) {
if( taskRunner!=null ) { if( taskRunner!=null ) {
try { try {
taskRunner.wakeup(); taskRunner.wakeup();
@ -148,6 +148,16 @@ public class ActiveMQSessionExecutor implements Task {
} }
public boolean iterate() { public boolean iterate() {
// Deliver any messages queued on the consumer to their listeners.
for (Iterator i = this.session.consumers.iterator(); i.hasNext();) {
ActiveMQMessageConsumer consumer = (ActiveMQMessageConsumer) i.next();
if( consumer.iterate() ) {
return true;
}
}
// No messages left queued on the listeners.. so now dispatch messages queued on the session
MessageDispatch message = messageQueue.dequeueNoWait(); MessageDispatch message = messageQueue.dequeueNoWait();
if( message==null ) { if( message==null ) {
return false; return false;

View File

@ -32,7 +32,7 @@ public class RedeliveryPolicy implements Cloneable, Serializable {
// +/-15% for a 30% spread -cgs // +/-15% for a 30% spread -cgs
protected double collisionAvoidanceFactor = 0.15d; protected double collisionAvoidanceFactor = 0.15d;
protected int maximumRedeliveries = 5; protected int maximumRedeliveries = 6;
protected long initialRedeliveryDelay = 1000L; protected long initialRedeliveryDelay = 1000L;
protected static Random randomNumberGenerator; protected static Random randomNumberGenerator;
protected boolean useCollisionAvoidance = false; protected boolean useCollisionAvoidance = false;

View File

@ -183,6 +183,10 @@ abstract public class PrefetchSubscription extends AbstractSubscription{
prefetchExtension--; prefetchExtension--;
} }
} }
public void afterRollback() throws Exception {
super.afterRollback();
}
}); });
} }
index++; index++;

View File

@ -57,7 +57,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
protected RedeliveryPolicy getRedeliveryPolicy() { protected RedeliveryPolicy getRedeliveryPolicy() {
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy(); RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setInitialRedeliveryDelay(1000); redeliveryPolicy.setInitialRedeliveryDelay(1000);
redeliveryPolicy.setMaximumRedeliveries(2); redeliveryPolicy.setMaximumRedeliveries(3);
redeliveryPolicy.setBackOffMultiplier((short) 2); redeliveryPolicy.setBackOffMultiplier((short) 2);
redeliveryPolicy.setUseExponentialBackOff(true); redeliveryPolicy.setUseExponentialBackOff(true);
return redeliveryPolicy; return redeliveryPolicy;
@ -82,7 +82,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
try { try {
log.info("Message Received: " + message); log.info("Message Received: " + message);
counter++; counter++;
if (counter <= 3) { if (counter <= 4) {
log.info("Message Rollback."); log.info("Message Rollback.");
session.rollback(); session.rollback();
} else { } else {
@ -119,24 +119,26 @@ public class MessageListenerRedeliveryTest extends TestCase {
} catch (InterruptedException e) { } catch (InterruptedException e) {
} }
// first try
assertEquals(1, listener.counter); // first try.. should get 2 since there is no delay on the
// first redeliver..
assertEquals(2, listener.counter);
try { try {
Thread.sleep(1000); Thread.sleep(1000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
} }
// second try (redelivery after 1 sec) // 2nd redeliver (redelivery after 1 sec)
assertEquals(2, listener.counter); assertEquals(3, listener.counter);
try { try {
Thread.sleep(2000); Thread.sleep(2000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
} }
// third try (redelivery after 2 seconds) - it should give up after that // 3rd redeliver (redelivery after 2 seconds) - it should give up after that
assertEquals(3, listener.counter); assertEquals(4, listener.counter);
// create new message // create new message
producer.send(createTextMessage(session)); producer.send(createTextMessage(session));
@ -148,7 +150,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
// ignore // ignore
} }
// it should be committed, so no redelivery // it should be committed, so no redelivery
assertEquals(4, listener.counter); assertEquals(5, listener.counter);
try { try {
Thread.sleep(1500); Thread.sleep(1500);
@ -156,7 +158,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
// ignore // ignore
} }
// no redelivery, counter should still be 4 // no redelivery, counter should still be 4
assertEquals(4, listener.counter); assertEquals(5, listener.counter);
session.close(); session.close();
} }
@ -185,7 +187,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
} }
// first try // first try
assertEquals(1, listener.counter); assertEquals(2, listener.counter);
try { try {
Thread.sleep(1000); Thread.sleep(1000);
@ -193,7 +195,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
} }
// second try (redelivery after 1 sec) // second try (redelivery after 1 sec)
assertEquals(2, listener.counter); assertEquals(3, listener.counter);
try { try {
Thread.sleep(2000); Thread.sleep(2000);
@ -201,7 +203,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
} }
// third try (redelivery after 2 seconds) - it should give up after that // third try (redelivery after 2 seconds) - it should give up after that
assertEquals(3, listener.counter); assertEquals(4, listener.counter);
// create new message // create new message
producer.send(createTextMessage(session)); producer.send(createTextMessage(session));
@ -213,7 +215,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
// ignore // ignore
} }
// it should be committed, so no redelivery // it should be committed, so no redelivery
assertEquals(4, listener.counter); assertEquals(5, listener.counter);
try { try {
Thread.sleep(1500); Thread.sleep(1500);
@ -221,7 +223,7 @@ public class MessageListenerRedeliveryTest extends TestCase {
// ignore // ignore
} }
// no redelivery, counter should still be 4 // no redelivery, counter should still be 4
assertEquals(4, listener.counter); assertEquals(5, listener.counter);
session.close(); session.close();
} }

View File

@ -70,9 +70,15 @@ public class RedeliveryPolicyTest extends JmsTestSupport {
assertEquals("1st", m.getText()); assertEquals("1st", m.getText());
session.rollback(); session.rollback();
// Show re-delivery delay is incrementing. // No delay on first rollback..
m = (TextMessage)consumer.receive(100);
assertNotNull(m);
session.rollback();
// Show subsequent re-delivery delay is incrementing.
m = (TextMessage)consumer.receive(100); m = (TextMessage)consumer.receive(100);
assertNull(m); assertNull(m);
m = (TextMessage)consumer.receive(500); m = (TextMessage)consumer.receive(500);
assertNotNull(m); assertNotNull(m);
assertEquals("1st", m.getText()); assertEquals("1st", m.getText());
@ -117,7 +123,12 @@ public class RedeliveryPolicyTest extends JmsTestSupport {
assertEquals("1st", m.getText()); assertEquals("1st", m.getText());
session.rollback(); session.rollback();
// Show re-delivery delay is incrementing. // No delay on first rollback..
m = (TextMessage)consumer.receive(100);
assertNotNull(m);
session.rollback();
// Show subsequent re-delivery delay is incrementing.
m = (TextMessage)consumer.receive(100); m = (TextMessage)consumer.receive(100);
assertNull(m); assertNull(m);
m = (TextMessage)consumer.receive(500); m = (TextMessage)consumer.receive(500);

View File

@ -45,7 +45,7 @@ public class RollbacksWhileConsumingLargeQueueTest extends
private CountDownLatch latch; private CountDownLatch latch;
private Throwable failure; private Throwable failure;
public void xtestWithReciever() throws Throwable { public void testWithReciever() throws Throwable {
latch = new CountDownLatch(numberOfMessagesOnQueue); latch = new CountDownLatch(numberOfMessagesOnQueue);
Session session = connection.createSession(true, 0); Session session = connection.createSession(true, 0);
MessageConsumer consumer = session.createConsumer(destination); MessageConsumer consumer = session.createConsumer(destination);
@ -148,11 +148,11 @@ public class RollbacksWhileConsumingLargeQueueTest extends
int value = deliveryCounter.incrementAndGet(); int value = deliveryCounter.incrementAndGet();
if (value % 2 == 0) { if (value % 2 == 0) {
log.info("Rolling Back message: " + value + " id: " + msgId + ", content: " + msgText); log.info("Rolling Back message: " + ackCounter.get() + " id: " + msgId + ", content: " + msgText);
throw new RuntimeException("Dummy exception on message: " + value); throw new RuntimeException("Dummy exception on message: " + value);
} }
log.info("Received message: " + value + " id: " + msgId + ", content: " + msgText); log.info("Received message: " + ackCounter.get() + " id: " + msgId + ", content: " + msgText);
ackCounter.incrementAndGet(); ackCounter.incrementAndGet();
latch.countDown(); latch.countDown();
} }