ibm-messaging / mq-jms-spring

Components to assist MQ JMS integration with Spring frameworks
Apache License 2.0
186 stars 102 forks source link

Application waits for 30 seconds before stopping when using readAheadAllowed=1 #106

Closed rimvydas-pranciulis closed 5 months ago

rimvydas-pranciulis commented 5 months ago

I'm using Spring DefaultMessageListenerContainer for reading messages from IBM MQ and noticed that after enabling "read ahead" application doesn't stop immediately, but for waits for time configured under spring.jms.listener.receive-timeout property (it is set to 30 seconds by default in mq-jms-spring-boot-starter library). Without "read ahead" applications stops immediately.

Code example:

@JmsListener(destination = "queue:///QUEUE_NAME?targetClient=1&readAheadAllowed=1")
public void consumeMessage(Message message) {
   ...
}

Additional Spring JMS configuration:

spring.jms.listener.session.transacted = false
spring.jms.listener.session.acknowledge-mode = dups_ok

Logs:

2024-01-25T11:24:40,039 TRACE [org.springframework.jms.JmsListenerEndpointContainer#0-1] org.springframework.jms.listener.DefaultMessageListenerContainer {} - Consumer [com.ibm.mq.jakarta.jms.MQQueueReceiver@171ee892] of session [Cached JMS Session: ...] did not receive a message

---
~2024-01-25T11:24:49 - Application shutdown is triggered
---

2024-01-25T11:25:19,013 TRACE [org.springframework.jms.JmsListenerEndpointContainer#0-1] org.springframework.jms.listener.DefaultMessageListenerContainer {} - Consumer [com.ibm.mq.jakarta.jms.MQQueueReceiver@171ee892] of session [Cached JMS Session: ...] did not receive a message
2024-01-25T11:25:19,104 DEBUG [SpringApplicationShutdownHook] org.springframework.jms.connection.CachingConnectionFactory {} - Closing shared JMS Connection:
2024-01-25T11:25:19,217 DEBUG [SpringApplicationShutdownHook] org.springframework.jms.listener.DefaultMessageListenerContainer {} - Shutting down JMS listener container
2024-01-25T11:25:19,218 DEBUG [SpringApplicationShutdownHook] org.springframework.jms.listener.DefaultMessageListenerContainer {} - Waiting for shutdown of message listener invokers
2024-01-25T11:25:19,219 DEBUG [org.springframework.jms.JmsListenerEndpointContainer#0-1] org.springframework.jms.listener.DefaultMessageListenerContainer {} - Lowered scheduled invoker count: 0
2024-01-25T11:25:19,229 TRACE [org.springframework.jms.JmsListenerEndpointContainer#0-1] org.springframework.jms.connection.CachingConnectionFactory {} - Logical close of cached JMS Session failed - discarding it
com.ibm.msg.client.jakarta.jms.DetailedIllegalStateException: JMSCC0020: This session is closed.
...
Process finished with exit code 130
ibmmqmet commented 5 months ago

This must be missing some information because

I tried this both with the year-old 3.0.3 version and a current version of the starter, and the trace shows the wait time being respected both with and without the readahead setting.

rimvydas-pranciulis commented 5 months ago

I'm shutting down application manually, that's why JMS listener stops. Problem is that with "read ahead" JMS listener doesn't stop immediately after application shutdown is triggered, but it waits for extra 30 seconds (value of spring.jms.listener.receive-timeout) and this prevents application from shutting down gracefully for this amount of time.

It is problem only with application shutdown, everything is good while it's running.

ibmmqmet commented 5 months ago

That's a rather better description. But I don't actually see any difference in behaviour when I added some exit code - readahead or not. Spring still appears always to wait for the timeout in its shutdown loop.

There's nothing this package can do to affect how either Spring works or how the MQ client code works; though it's not clear which piece has any "issue". I imagine that Spring is not aware of any background threads that the MQ client might have started up. Perhaps that's affecting the processing.

One thing I did try was to reset the timeout value in the code I used to exit from a listener. That did seem to speed up the exiting:

JmsListenerEndpointRegistry reg = MyApplication.context                 
          .getBean("org.springframework.jms.config.internalJmsListenerEndpointRegistry",  
                            JmsListenerEndpointRegistry.class);
if (reg != null) {
  Set<String> ids = reg.getListenerContainerIds();
  for (String id:ids) {
    MessageListenerContainer cont = reg.getListenerContainer(id);
     if (cont instanceof DefaultMessageListenerContainer) {  
        ((DefaultMessageListenerContainer)cont).setReceiveTimeout(1);
     }
  }
}
rimvydas-pranciulis commented 5 months ago

Does code you pasted runs before or after shutdown of your application is triggered?

ibmmqmet commented 5 months ago

it runs before a System.exit(SpringApplication.exit(Application.context,... used when I want to break out of the app.

rimvydas-pranciulis commented 5 months ago

Ok, so that it explains why it didn't worked when I tried to put into shutdown hook. I don't stop my app with exit statement, it is running on Kubernetes and that is where its lifecycle is controlled, so this workaround won't work.

Anyway, if that is not an issue of this library, but more related to core IBM MQ library then I'm closing this "issue". Thanks for taking a look.