0

We are using Spring CachingConnectionFactory to produce the message to a IBM MQ topic using Spring JmsTemplate We have set the sessionCacheSize as 50 :-

cachingConnectionFactoryBean
        .setSessionCacheSize(50));

Intermittently, i am seeing that JmsTemplate is taking more than 2-3 minute to publish the message to topic. Generally, publishing operation gets completed within some miliseconds.

I am suspecting if based on high load during some time, all sessions (limit is 50) are getting exhausted and this high latency message publication is waiting for session to be available from session pool.

How can i find out if all my session at that point of time was doing work and no session was available to take this new request which leads to long publishing time ?

Note :- this issue occurs intermittently as we got to know from our consumers little late in time and i am not sure taking thread dump will help at later point of time.

Neer1009
  • 304
  • 1
  • 5
  • 18

2 Answers2

0

Without a caching or pooling connection factory, Spring JMS will create a new connection and session to the destination for every message sent. This is fine if you send a message once every 10 minutes, but is not satisfactory for a high message volume application. So using a caching/pooling connection factory is very common.

Generally, however, the pooling connection factory would only handle the one connection for the Spring JMS message producer. Hence, the default session size is one. I would normally expect to see something like:

<bean class="com.ibm.mq.jms.MQConnectionFactory" id="dest.mqConnectionFactory">
    <property name="connectionNameList" value="10.0.0.171(1414)"/>
    <property name="queueManager" value="WARP.QMGR"/>
    <property name="channel" value="SYSTEM.DEF.SVRCONN"/>
    <property name="transportType" value="1"/>
    <property name="clientReconnectOptions" value="67108864"/>
</bean>

<bean class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter" id="dest.mqUserCred">
    <property name="targetConnectionFactory" ref="dest.mqConnectionFactory"/>
    <property name="username" value="XXXX"/>
    <property name="password" value="XXXX"/>
</bean>

<bean class="org.apache.activemq.jms.pool.PooledConnectionFactory" destroy-method="stop" id="dest.pooledConnectionFactory" init-method="start">
    <property name="idleTimeout" value="${idleTimeout}"/>
    <property name="connectionFactory" ref="dest.mqUserCred"/>
</bean>

<bean class="org.apache.camel.component.jms.JmsComponent" id="dest">
    <property name="configuration">
        <bean class="org.apache.camel.component.jms.JmsConfiguration">
            <property name="connectionFactory" ref="dest.pooledConnectionFactory"/>
        </bean>
    </property>
</bean>

In the example above, the PooledConnectionFactory would have one connection and that one JMS connection would have one JMS session. Note that the CachingConnectionFactory extends the SingleConnectionFactory. So you have a single JMS connection that can have multiple JMS sessions. You can't share that single JMS connection across threads.

Any JMS message producer will first ask for a JMS connection, and then request a JMS session from the connection. I'm not understanding how you can be sending 50 messages on those 50 JMS sessions with a single JMS connection. If you are trying to share the one CachingConnectionFactory across multiple message producers, then only one message producer can be active at a time, and it will only use one session.

In my example, I would see a TCP/IP connection from my computer to 10.0.0.171(1414). That JMS connection would have one session. I don't know of any way to have the single JMS connection shared across 50 messages producers on 50 JMS sessions. Obviously, you could write you own code that would do that, but that is not how Spring JMS works.

Doug Grove
  • 962
  • 5
  • 5
  • I read this information on IBM MQ page : - https://www.ibm.com/docs/en/ibm-mq/8.0?topic=applications-jms-model It says :- A Destination, ConnectionFactory, or Connection object can be used concurrently by different threads of a multithreaded application, but a Session, MessageProducer, or MessageConsumer object cannot be used concurrently by different threads. If session cannot be used to send message concurrently, then what is the use case for increasing the limit of session cache to more than 1 ? – Neer1009 Oct 18 '21 at 07:46
  • I apologize, the JMS connections can be shared by threads, but the sessions are thread local. If you write you own bespoke code, you can certainly use multiple sessions. I suppose that the only way to know how many sessions are active would be to get the thread dump. Have you ever used the IBM client code logging? Would you be interested in that? – Doug Grove Oct 19 '21 at 14:17
0

Since version 5.3.7, the factory has

    /**
     * Return a current session count, indicating the number of sessions currently
     * cached by this connection factory.
     * @since 5.3.7
     */
    public int getCachedSessionCount() {

See https://github.com/spring-projects/spring-framework/issues/26811

However, if the cache is empty, a new session is always created; the size is simply used to determine if the session should be returned to the cache or physically closed. It is not a limit. See the logic in getSession:

    /**
     * Checks for a cached Session for the given mode.
     */
    @Override
    protected Session getSession(Connection con, Integer mode) throws JMSException {
        if (!this.active) {
            return null;
        }

        Deque<Session> sessionList = this.cachedSessions.computeIfAbsent(mode, k -> new ArrayDeque<>());
        Session session = null;
        synchronized (sessionList) {
            if (!sessionList.isEmpty()) {
                session = sessionList.removeFirst();
            }
        }
        if (session != null) {
            if (logger.isTraceEnabled()) {
                logger.trace("Found cached JMS Session for mode " + mode + ": " +
                        (session instanceof SessionProxy ? ((SessionProxy) session).getTargetSession() : session));
            }
        }
        else {
            Session targetSession = createSession(con, mode);
            if (logger.isDebugEnabled()) {
                logger.debug("Registering cached JMS Session for mode " + mode + ": " + targetSession);
            }
            session = getCachedSessionProxy(targetSession, sessionList);
        }
        return session;
    }
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • :- As i am using cachingConnectionFactory to create connection, i don't find any option to provide limit on connection to achieve concurrency. My understanding was that connection. + session combination can be used to increase the message producers. e.g. if i have 1 connection from cachingConnFactory and 50 sessions , it means i can send 50 parallel message to topic. Is my understanding correct ? If not, how can i achieve the concurrency to send multiple message to MQ topic to handle the high load. – Neer1009 Oct 19 '21 at 16:49
  • As I said, the cache size is not a limit; whenever the cache is empty, a new session is created; if that doesn't help, the bottleneck is somplace else - not in Spring. – Gary Russell Oct 19 '21 at 17:05
  • :- How can i increase the concurrency so that concurrent threads can publish to same topic ? e.g. if 2 threads wanted to push the message to same topic and both threads are using same JmsTemplate Bean to publish it, setting 2 session count (cachingConnectionFactoryBean .setSessionCacheSize(50)); ) won't help here ? – Neer1009 Oct 20 '21 at 10:35
  • One more time, the cache size does not affect concurrency, only performance. If the cache is empty a new session is created by each thread as needed. The size determines whether the session is returned to the cache after use so, increasing the size, will generally improve performance, but it has no effect on concurrency. If you are not getting the performance you need, and you have increased the cache size, then something else is limiting your performance. – Gary Russell Oct 20 '21 at 13:14
  • thanks Gary. will check the code – Neer1009 Oct 21 '21 at 11:52