Search code examples
javaibm-mqinstancesresource-leak

How to avoid too many IBM MQ channel instances?


I've built a small Java program that uses the MQ client classes to talk to a Websphere MQ server and a queue thereon. What I want is to query the size, err depth, of a certain queue at regular intervals, e.g. every 10 seconds.

What I do, schematically:

  • qm = new MQQManager()
  • q = qm.accessQueue()
  • depth = q.getCurrentDepth()
  • (do something with depth)
  • q.close()
  • qm.close()

This works like a charm!

Out of laziness, I wrote my code to go through all 6 steps of the above sequence each time. So I ended up repeating this cycle every 10 seconds. Within about half an hour, the folks who monitor the server told me I had 153 instances open on it. Clearly, simply doing close() on the queue and QM aren't sufficient to clean up after myself.

I'm going to do the obvious fix and hold on to the QM and queue for the program's lifetime. But could somebody please tell me why my previous approach is leaking resources, and what I could do about it if I chose to follow that path?


UPDATE (2017-01-25)

Timing and Acceptance

Roger has provided some beautiful code and some useful explanations on the topics of performance and uncommitted messages. That's cool, but Eugene's answer was exactly (and minimally) sufficient to solve my problem, and he was a bit quicker. My little app is now doing exactly what I want after I simply changed close() to disconnect() based on Eugene's advice. Now I'm a bit torn about who should get the checkmark. My apologies to both of you!

I got exactly what I wanted and I think I'm done with this question but I should add that I appreciate Roger's thoughtful answer for the useful information it provides to any possible future other reader of this question. Hopefully other people will listen to him rather than follow my lead.

Performance

To be honest, I don't give a damn about performance. The server is much fatter than needed for what it's currently doing, and my monitoring client (the topic of this question) runs on a development machine that's usually empty. I hope it will have served its purpose within a day or two, then I'll throw it away. In the meantime, if one connect every 10s causes a performance problem, they should load balance to a Raspberry Pi or something.

Uncommitted messages

I think this is a non-problem as well. The sending application enqueues in small batches of about 1-5 messages within a few milliseconds and immediately commits. The receiving application (a client I wrote, and it may well have problems) continuously listens on the queue, receives and acknowledges (commits) each message individually. So I'm reasonably sure there will never be more than 1, possibly 5 unacknowledged messages. This number is insignificant compared to the parameters of...

my actual problem,

which involves a handful to a few hundred messages suffering processing delays between 5 and 20 minutes at about the same time every morning. I suspect my receiving client is being stalled by a "hanging" network IO operation with something other than the MQS. One step I want to take in exploring this is to demonstrate that there is indeed a queue of waiting messages on the MQS, and it's not being picked up. I'd like to see when that queue starts to build up, how long it sits there and how quickly it subsides once my client wakes up again.

This connection sees between 2,000 and 10,000 messages a day with peaks corresponding to batch processes in the afternoon and evening, but the delays are happening only in the morning, often with pretty small volumes actually coming through. I'd like to see a log of queue sizes to get a better idea of what's going on. As a next step, I'll probably need to do more instrumentation in my receiving client.

Environment

For what it's worth, the server is running WSMQ 6, so my client uses blocking-and-waiting queue gets rather than the spiffy asynchronous notification process I would have preferred to use. My receiving client is a standalone C app running under RHEL in a VM.


Solution

  • Wow! Not very efficient code. The connect is by FAR the most expensive MQ API call. Also, getting the queue depth is meaningless. It gives you ALL uncommitted and committed messages in the queue. i.e. queue depth may say there is 5 messages in the queue but your program may only retrieve 3!! You would say MQ is broken but in reality, 2 messages are uncommitted, hence, unavailable for the consumer.

    If you really want to get and log the information every 10 seconds then STAY connected.

    Here's a better way of doing it:

    boolean working = true;
    int openOptions  = CMQC.MQOO_INQUIRE + CMQC.MQOO_FAIL_IF_QUIESCING;
    int depth = 0;
    MQQueueManager qm = null;
    MQQueue q = null;
    
    try
    {
       qm = new MQQueueManager("MQA1");
    
       while (working)
       {
          try
          {
             q = qm.accessQueue("TEST.Q1", openOptions);
             depth = q.getCurrentDepth();
    //         (do something with depth)
          }
          catch (MQException e)
          {
             System.out.println("CC = " + e.completionCode + " : RC=" + e.reasonCode);
             System.out.println(e.getLocalizedMessage() );
             working = false;
          }
          finally
          {
             if (q != null)
             {
                q.close();
                q = null;
             }
          }
    
          try
          {
             if (working)
                Thread.sleep(10*1000); // time in milliseconds
          }
          catch (InterruptedException ie) {}
       }
    }
    catch (MQException e)
    {
       System.out.println("CC = " + e.completionCode + " : RC=" + e.reasonCode);
       System.out.println(e.getLocalizedMessage() );
    }
    finally
    {
       try
       {
          if (q != null)
             q.close();
       }
       catch (MQException e)
       {
          System.out.println("CC = " + e.completionCode + " : RC=" + e.reasonCode);
          System.out.println(e.getLocalizedMessage() );
       }
    
       try
       {
          if (qm != null)
             qm.disconnect();
       }
       catch (MQException e)
       {
          System.out.println("CC = " + e.completionCode + " : RC=" + e.reasonCode);
          System.out.println(e.getLocalizedMessage() );
       }
    }