Mission:Messaging: Easing administration and debugging with circular queues

The standard behavior of a queue when it fills up is to stop accepting messages. New messages cannot be enqueued and it is up to the application to determine whether to stop and wait for the queue to drain or to discard the messages. For many use cases though, it would be convenient if the newest messages pushed the oldest messages off of the queue. The result would be that the queue always contained the latest messages rather than the oldest ones and the application or channel never blocks. This installment of Mission: Messaging demonstrates how to achieve this using the built-in features of IBM® WebSphere® MQ, SupportPac MA01, and some sample "glue" code written in Perl. This content is part of the IBM WebSphere Developer Technical Journal.

T.Rob Wyatt, Senior Managing Consultant, IBM

T.Rob WyattT.Rob Wyatt is a Senior Managing Consultant with IBM Software Services for WebSphere who assists customers with the administration, architecture, and security of WebSphere MQ. Recently he has focused on WebSphere MQ security, publishing in the IBM WebSphere Developer Technical Journal and presenting at the IMPACT and European Transaction and Messaging conferences. T.Rob also hosts The Deep Queue, a monthly WebSphere MQ security podcast.



30 March 2011

Also available in Chinese

Why a circular queue?

The classic use case for queuing is to buffer messages when they are produced faster than they are consumed. It's an elegant system that works wonderfully right up to the moment that the queue fills up. At that point, the system changes from asynchronous behavior to synchronous behavior, where the producer can enqueue messages only as fast as the consumer can dequeue them. In most cases, this is the desired behavior and is treated as a tuning problem: increase MAXDEPTH until the queue can absorb the highest peak loads.

But there are a number of cases in which it would be more useful for the queue to always accept new messages, even if that means discarding the oldest ones. The classic example is that of event messages. I always advise my clients to enable authorization events so that any authorization errors can be easily diagnosed. Many people like to enable other events as well. If there is no monitoring agent to consume these events in real time, they collect in the event queues. Once the event queues fill up, new event messages for that queue are discarded. This leads to a catch-22 situation: if you wait until you need the events to enable them, the situation you need to diagnose has already occurred and you have missed the opportunity to capture an event message -- but if you enable events in anticipation of a problem, the queue is likely to be full when the trouble occurs and, again, you lose the opportunity to capture the event message.

This article demonstrates how to configure a queue so that as it approaches being full, messages at the head of the queue are deleted. This makes room for new messages as they arrive so that, at any point in time, the queue always has the latest messages instead of the oldest. Because this behavior resembles that of circular logs, I have dubbed the concept "circular queues" and over the years I've found it to be extremely useful for administration and debugging.


Overview

The configuration to support circular queues consists of several components:

  • A trigger monitor provides the ability to react to the changing depth of the queue.
  • A process is required for the trigger monitor to work.
  • The ChompQ.pl sample script included with this article contains the logic that determines how many messages to delete.
  • The q program from SupportPac MA01 is used to delete the messages.

We will take a look at each of these in turn. If you are not already familiar with triggering, have no fear! We will cover all that is required to get the ChompQ example up and running and I'll provide links to additional reading on triggering in the Resources section.

But before we look at the configuration, let's go over what happens when the ChompQ script is triggered:

  1. The queue depth exceeds the threshold set by TRIGDEPTH; a trigger fires.
  2. The trigger monitor picks up the trigger message and retrieves the process object pointed to by the queue's PROCESS attribute.
  3. The trigger monitor starts ChompQ.pl, as specified in the process object, and passes the trigger message as a command line argument.
  4. ChompQ.pl reads the queue and queue manager names from the trigger message.
  5. ChompQ.pl queries QDEPTHHI, CURDEPTH, and MAXDEPTH of the triggered queue to determine how many messages to delete from the queue.
  6. ChompQ.pl calls the Q program to delete the messages, and passes it the queue, queue manager, and number of messages to delete.
  7. The Q program deletes the messages and returns control to ChompQ.pl.
  8. ChompQ.pl re-enables triggering on the queue and exits.
  9. The trigger monitor waits for another trigger message.

This sequence of events repeats each time the queue depth builds beyond the threshold configured in TRIGDEPTH. As a protection against an infinite trigger loop, ChompQ.pl will always delete at least one message. The exact behavior and timing can be adjusted by varying the values of QDEPTHHI, TRIGDEPTH, and MAXDEPTH. What's more, ChompQ can be configured on multiple queues using the same process definition and trigger monitor, and each queue can have different settings.

Now that we've covered the basics, let's roll up our sleeves and dive into the details.


Prerequisites

MQ objects

Before you do anything, you need a queue manager and some queues. For the purpose of this article, assume you have a queue manager named PLUTO (but you can do this exercise on any queue manager you happen to have available). Define a local queue named CHOMPQ.TEST to play around with and an initiation queue named CHOMPQ.INIT to serve it. This will ensure that you do not interfere with any triggering that might already be set up on the test queue manager. Go ahead and define these now:

Listing 1
DEFINE QL(CHOMPQ.INIT) +
       DESCR('Initiation queue for testing ChompQ.pl') +
       REPLACE

DEFINE QL('CHOMPQ.TEST') +
       DESCR('Local queue for testing ChompQ.pl') +
       INITQ('CHOMPQ.INIT') +
       TRIGGER +
       TRIGTYPE(DEPTH) +
       TRIGDPTH(900) +
       MAXDEPTH(1000) +
       PROCESS('CHOMPQ') +
       QDEPTHHI(80) +
       REPLACE

You will also need to define a process object, but the exact definition varies by platform so it will be covered later.

SupportPac MA01

You need to download SupportPac MA01, if you do not already have it. The Q program is the "Swiss army knife" of WebSphere MQ utilities and can read and write messages on queues or topics and manipulate all of the message options and attributes that are available in the API. It comes with executables compiled for a variety of platforms, so all you need to do is unzip the appropriate version and you are ready to go. I usually put Q in the WebSphere MQ bin directory but you can put it anywhere in the queue manager's PATH. If you haven't used Q before, take some time to become more familiar with it before proceeding on with this exercise.

Perl

Perl is a standard component of all UNIX® and Linux® distributions where WebSphere MQ is supported. If you are running on Windows®, you might need to obtain and install a copy of Perl before proceeding. Also for Windows installations, make sure that the Perl executable is in the PATH of the queue manager before proceeding. You can test this by signing on as the WebSphere MQ service account, opening a command window and typing perl –v. If Perl is installed and in the PATH, you will see some version information displayed.

Dead letter queue

The trigger monitor will place rejected trigger messages onto the dead letter queue (DLQ). However, newly created queue managers are not configured to use a dead letter queue. Although an object named SYSTEM.DEAD.LETTER.QUEUE exists, it is necessary to alter the queue manager's DEADQ attribute with the name of the dead letter queue in order to enable it. Check that the queue manager you will be using has enabled DLQ processing.


A short course on triggering

The purpose of triggering is to enable the queue manager to initiate external actions based on the arrival of messages. Queue attributes determine when the trigger is fired and a process definition contains the command line to be executed in response to the trigger. A special program called a trigger monitor receives the trigger message from the queue manager, reads the process definition, and then runs the command line task specified there. The trigger monitor listens for trigger messages on an initiation queue. Initiation queues are ordinary local queues. There is no attribute of an initiation queue that causes it to be an initiation queue; you define it like any other local queue. However, the queue that is triggered has an INITQ attribute which must contain the name of the initiation queue in order for the queue manager to generate trigger messages.

WebSphere MQ provides all the components to enable triggering but it is up to the administrator to configure them, including starting the trigger monitor. In production, the trigger monitor would normally be started as a queue manager service and its output would be redirected to log files. It is much more convenient during the testing phase to run the trigger monitor as a foreground process because all of the diagnostic messages will be visible in the command session window. The trigger monitor supplied with WebSphere MQ is called runmqtrm, and if you are signed on as the administrative account (usually mqm or MUSR_MQADMIN) then runmqtrm should be in your path. To start runmqtrm you will need to pass it the names of the queue manager and initiation queue:

runmqtrm -m PLUTO -q CHOMPQ.INIT

If the PLUTO queue manager is running and the initiation queue is defined, you should see a message stating that the trigger monitor is started and waiting for a trigger message.


Define the process object

The process object tells the trigger monitor what command to execute. The exact definition depends on the platform of your server. The examples below work for Windows and UNIX, although you may need to alter them slightly if you have a non-default installation directory.

Listing 2
* Windows process definition for ChompQ:
DEFINE PROCESS(CHOMPQ) +
       APPLICID('perl C:\Progra~1\IBM\WEBSPH~1\exits\ChompQ.pl') +
       ENVRDATA('>>C:\Progra~1\IBM\WEBSPH~1\exits\ChompQ.log 2>&1 &') +
       REPLACE

* Unix process definition for ChompQ:
DEFINE PROCESS(CHOMPQ) +
       APPLICID('/var/mqm/exits/ChompQ.pl') +
       ENVRDATA('>>/var/mqm/exits/ChompQ.log 2>&1 &') +
       REPLACE

Notice that the process definitions shown assume that the executable program (perl for Windows and the ChompQ.pl script for UNIX) is in the queue manager's PATH. You can provide a fully-qualified path as long as the APPLICID string does not exceed 256 characters in length.

It is also worth pointing out that the process definitions above are designed to start ChompQ.pl in the background. This approach enables several instances of ChompQ.pl to run simultaneously, which is customary for triggered programs. Removing the ampersand (&) character from the end of the ENVRDATA field causes ChompQ.pl to be started in the foreground, which causes the trigger monitor to wait for ChompQ.pl to complete before reading the next trigger message. One strategy for testing triggered programs is to run them in the foreground (without the & at the end of the ENVRDATA field) so that the trigger monitor detects and reports any problems. Once the program is working as expected, restore the ampersand to the end of the ENVRDATA field so that the trigger monitor can run multiple concurrent instances of ChompQ.pl and thus be better able to keep up with volume on busy queue managers. For our test with only one triggered queue, there is almost no difference in behavior between these options. It is only when there are multiple triggered queues using the same initiation queue and trigger monitor that this distinction becomes relevant.


The ChompQ.pl script

All the pieces are now in place except for the ChompQ.pl script. Each of the script’s sections will be covered individually. To save time cutting and pasting them all together, the full listing is available for download. The entire listing contains 117 lines, however only 43 of these are actual code, the rest is documentation.

If you do not care about how the Perl script works and just want to install it, skip to the next section.

Preamble

The first part of the program pulls in a few Perl classes and initializes variables:

#!/user/bin/perl –w

Full listing
Code that was not relevant to this discussion was omitted from these examples so remember to download the full listing.

The first line of a Perl program is usually the shebang line. (A "shell" character followed by a "bang" is a "shebang.") This line tells the shell which interpreter should be used to execute the script. The fully-qualified path to the executable must be provided. You can also provide command line arguments, and in this example the Perl warnings feature has been enabled.

Windows will ignore the shebang line but it is usually left in for portability. On other platforms, make sure the shebang line points to where your Perl executable actually lives or else the script will not run. On UNIX or Linux, you can type which perl to determine the correct path.

Listing 3
use strict;
use constant VERSION  =>  3;
use constant TESTING  =>  1; 
	
TESTING && print "=" x 60, "\n$0: ", scalar(localtime), "\n";

Running with use strict enabled is always recommended (Listing 3). The TESTING constant is used to enable debugging. Set this constant to a non-zero value to enable diagnostic messages to be printed or set it to zero to suppress them. The last line shown demonstrates how this is used as a switch throughout the code. From this point forward, lines that are purely diagnostic are omitted so that we can concentrate on just those that contribute directly to performing program functions.

Listing 4
my ($Cmd, 
    $Resp, 
    %TrigMsg, 
    );	

# Program to delete messages.  Code expects q from MA01 SupportPac
my $Prog  = 'q';

Next, you set up global variables for the command to be executed, a buffer for response messages, and a hash that will contain the fields from the trigger message (Listing 4). The $Prog variable is used to locate the Q program. If it is in the PATH, then only the program name is required. Otherwise, the fully-qualified program name would be placed here.

Listing 5
# Trigger message field names & order
my @TrigFields = qw(StrucId Version QName ProcessName 
	TriggerData ApplType ApplId EnvData UserData QMgrName);

The @TrigFields array contains the names of the trigger message fields, in their proper order. You will use these later as keys to the %TrigMsg hash.

Some time ago I modified ChompQ.pl to work on Windows as well as UNIX and Linux environments and this required eliminating quotes from certain command lines. To determine which flavor of operating system is in use, the system PATH variable is inspected. The line of code below searches the PATH variable looking for a semicolon. If one is found then the system is assumed to be Windows and $Quotes is set to an empty string, otherwise $Quotes is set to the double quote character.

my $Quotes = $ENV{'PATH'} =~ /;/ ? '' : '"';

Parsing command-line arguments

At this point, all the necessary variables have been set and you can begin with the main part of the program. The trigger monitor passes an intact TMC2 message to the triggered program (Listing 6). This message is a structure containing several fields, but appears to the triggered program to be a single command line parameter. This section of ChompQ.pl iterates over command line arguments looking for TMC2 structures. If a TMC2 is found, it is parsed into its constituent fields.

Listing 6
foreach (@ARGV) {
    TESTING && print "Parm='$_'\n";
    if (/^TMC    2/) {
       (@TrigMsg{@TrigFields}) = unpack("a4 a4 a48 a48 a64 a4 a256 a128 a128 a48", $_);
        $TrigMsg{'$0'} = $0;  # Diagnostic
        foreach my $Key (sort keys %TrigMsg) {
            $TrigMsg{$Key} =~ s/\s+$//;  # Delete trailing blanks
            TESTING && printf "%11.11s = %s\n\n", $Key, defined $TrigMsg{$Key} ? 
		$TrigMsg{$Key} : "NULL";
        }
    }
}

The outer foreach loop iterates over all of the command-line parameters in the @ARGV array looking for a trigger message. It detects these based on the existence of the StrucID and Version fields at the start of the parameter. That's the "TMC 2" in the regular expression toward the top of the block. Since the TMC2 fields are all of fixed length, the unpack command is used to parse the structure into an array. In a bit of Perl shorthand, the array of field names and the array of parsed fields is combined into a hash. A special hash entry named $0 is then added, which contains the name and version of ChompQ.pl for debugging purposes.

The inner foreach loop iterates over the hash entries and strips trailing space characters from the values. WebSphere MQ always pads values with trailing spaces and these cause problems when passed to the operating system as commands. If TESTING is enabled, the hash entries are printed out in sorted order as the loop executes.

Before proceeding, the program must have received a valid trigger message, and that message must contain names of both the queue manager and queue that was triggered. If these values are not present, the program exits with a non-zero return code (Listing 7).

Listing 7
# Non-zero exit if we were triggered with something other than TMC2
exit 1 unless (exists $TrigMsg{'QName'} && $TrigMsg{'QMgrName'});

Inquire on queue attributes

Once the queue manager and queue names are known, it is possible to retrieve the attributes of the queue. Although it is possible to do this programmatically using the Perl MQSeries module, I wanted a solution that would be functional with as few dependencies as possible. Since runmqsc has to be available wherever a queue manager exists, ChompQ interacts with the queue manager objects by building the appropriate runmqsc command, executing it, and then parsing the output (Listing 8).

Listing 8
my @QAttrs = qw(CURDEPTH MAXDEPTH QDEPTHHI);
$Cmd = sprintf("echo %sdis ql('%s') %s%s | runmqsc %s 2>&1", $Quotes, $TrigMsg{QName}, 
join(' ', @QAttrs), $Quotes, $TrigMsg{QMgrName});

In the example, an array is used to hold the queue attributes of interest. Here, you are inquiring on the current and maximum queue depth, the maximum message length, and the value of QDEPTHHI. The sprintf function contains a template of the runmqsc command into which values are substituted. The most interesting value is $Quotes. Recall that in Windows, echo commands with parentheses need no quotes, while on various UNIX flavors they do. Since $Quotes either contains an empty string or a double quote character, you can use the same template for both operating systems. The command that is built will look like Listing 9.

Listing 9
echo "dis ql('CHOMPQ.TEST') CURDEPTH MAXDEPTH MAXMSGL QDEPTHHI" | runmqsc PLUTO 2>&1

The 2>&1 at the end of the command tells the operating system to send STDERR to the same file handle as STDOUT. Combining STDOUT and STDERR in this way prevents STDERR output from showing up on the system console and enables the program to parse it.

Listing 10
$Resp = `$Cmd`;
foreach (@QAttrs) {$Resp =~ /$_\((\d+)\)/ && do {$TrigMsg{$_} = $1};}

The command is then executed and the output captured in the $Resp variable (Listing 10). The text in $Resp is then searched for each of the queue attributes in the @QAttrs array. The regular expression captures the value of each attribute and places these in the same hash you used to hold the fields from the TMC2 message earlier.

Delete the messages

At this point, you have all that you need to calculate the number of messages to be deleted and call the Q program to do the actual deletions. The number of messages to delete is calculated as:

Current depth – ( (Max Depth * QDEPTHHI) / 100)

The queue manager interprets the value in QDEPTHHI as a percentage and permits only values between 0 and 100. This works for your purposes because a percentage gives you enough granularity. For example, with the default MAXDEPTH of 5,000, a QDEPTHHI value of 80 represents 4,000 messages and each percentage point above or below that represents a 50-message increment.

Listing 11
my $DeleteCount = $TrigMsg{'CURDEPTH'} - int( $TrigMsg{'MAXDEPTH'} * 
	$TrigMsg{'QDEPTHHI'} / 100) ; 
$DeleteCount = 1 if $DeleteCount < 1;
	
$Cmd = "$Prog -dn -q -I$TrigMsg{'QName'} -L$DeleteCount -m$TrigMsg{'QMgrName'} 
	-=0  2>&1";
$Resp = `$Cmd`;

The first line of code in Listing 11 calculates the number of messages to be deleted according to the formula described above. However there are a number of cases in which the resulting number could be less than one, the primary one of these being that TRIGDEPTH and QDEPTHHI are not correctly set in relation to one another. For example, if QDEPTHHI is set at 80 percent and MAXDEPTH is 5,000, then ChompQ.pl will attempt to keep the queue pruned to 4,000 messages. If TRIGDEPTH is set to 3,000 then ChompQ will calculate a negative number of messages to delete. If ChompQ.pl ended without taking any action, a new trigger would immediately fire, resulting in a loop. To prevent this, a line of code insures that $DeleteCount is always one or higher. The Q program is then executed with the values that were just calculated and the output captured to the $Resp variable.

Re-enable triggering on the queue

The last step is crucial because when a queue is triggered on depth, WebSphere MQ disables triggering when the trigger is fired. It is up to the triggered program to re-enable triggering on the queue before exiting.

Listing 12
$Cmd = "echo " . $Quotes . "alter ql('" . $TrigMsg{'QName'} . "') trigger" . $Quotes . 
	" | runmqsc " . $TrigMsg{QMgrName} . ' 2>&1';
$Resp = `$Cmd`;

An ALTER command is passed to runmqsc using the same technique described earlier. Since the command line has far fewer parameters, the code simply concatenates all the necessary values rather than using sprintf, as was done last time. The command line that is constructed looks like this:

echo "alter ql('CHOMPQ.TEST') trigger" | runmqsc PLUTO 2>&1

Again, note that on Windows platforms, the double quotes would be missing from the constructed command. Once constructed, the command is executed and the response captured to the $Resp variable.

Exit cleanly

The last thing to do is exit with a zero return code so that the trigger monitor will commit the GET it performed on the trigger message and wait for another:

exit 0; # Exit with Zero return code for Trigger Monitor


Install and test ChompQ

  1. Download the ChompQ.pl script. To install, simply transfer the program to the WebSphere MQ exits directory:
    • On Windows, this defaults to C:\Program Files\IBM\WebSphere MQ\exits.
    • On UNIX or Linux, the location is /var/mqm/exits. Be sure to transfer the file in text mode so that lined ends are converted. Also, make the script executable by running the command chmod 755 ChompQ.pl.
  2. Run the program just to verify that it is installed correctly. First, go to the exits directory. On Windows servers, the script should be directly executable if the .pl file extension has been associated with the Perl executable. In that case, just type ChompQ.pl to start the program. On UNIX or Linux servers, try ./ChompQ.pl instead.

    If the program is executable, you should see an error telling you that a TMC2 message is required to be passed on the command line.

  3. The only thing left to do at this point is to verify that the program and configurations all work together. You can do this by running a trigger monitor and dumping messages into the queue. The trigger monitor will produce its own diagnostic messages and the best way to see these is to run it as a foreground process in a command window or terminal session. (The examples that follow use a Linux server but the same commands will work on Windows.)

    First, open two command windows. One will be used to run the trigger monitor and watch its output. The other will be used to execute commands to run our tests. In the first window, start the trigger monitor with this command:

    runmqtrm -m PLUTO -q CHOMPQ.INIT

    The trigger monitor will start and display some diagnostic information (Listing 13).

    Listing 13
    mqm@linux-wmq-ams:~/Desktop> runmqtrm -m PLUTO -q CHOMPQ.INIT
    5724-H72 (C) Copyright IBM Corp. 1994, 2009.  ALL RIGHTS RESERVED.
    02/07/2011  09:30:37 PM : WebSphere MQ trigger monitor started.
    	
    __________________________________________________
    02/07/2011  09:30:38 PM : Waiting for a trigger message
  4. Next, use the Q program to place 950 messages on the queue. Recall that the queue was set to trigger at 900 messages. The MAXDEPTH is 1,000 and the QDEPTHHI is 80, resulting in a pruned depth of 800 messages (1000 * 80% = 800). When the trigger fires at the 900th message, a race condition ensues. Since Q is very fast, all 950 messages will be on the queue before ChompQ.pl can start up. This means that ChompQ.pl will calculate the prune count based on a total of 950 messages and you can expect the depth of the queue after pruning to be 800 (Listing 14).
    Listing 14
    mqm@linux-wmq-ams:~> q -mPLUTO -oCHOMPQ.TEST -M'#!950 Msg'
    MQSeries Q Program by Paul Clarke [ V5.0.0 Build:Jul 17 2008 ]
    Connecting ...connected to 'PLUTO'.
    mqm@linux-wmq-ams:~>

    In Listing 14, you see the use of Q to put messages on the queue and the response.

    Listing 15
    mqm@linux-wmq-ams:~> echo 'dis ql(CHOMPQ.TEST) curdepth' | runmqsc PLUTO
    5724-H72 (C) Copyright IBM Corp. 1994, 2009.  ALL RIGHTS RESERVED.
    Starting MQSC for queue manager PLUTO.
    	
         1 : dis ql(CHOMPQ.TEST) curdepth
    AMQ8409: Display Queue details.
       QUEUE(CHOMPQ.TEST)                      TYPE(QLOCAL)
       CURDEPTH(800)
    One MQSC command read.
    No commands have a syntax error.
    All valid MQSC commands were processed.
    mqm@linux-wmq-ams:~>

    When the queue depth is displayed, it does indeed contain exactly 800 messages. Earlier, I mentioned a race condition when the trigger fires. You placed 950 messages to the queue when it was empty. If you run the same command now that the queue has a depth of 800, the queue will fill much faster than ChompQ.pl can start and Q will receive a QFull error (Listing 16).

    Listing 16
    mqm@linux-wmq-ams:~> q -mPLUTO -oCHOMPQ.TEST -M'#!950 Msg'
    MQSeries Q Program by Paul Clarke [ V5.0.0 Build:Jul 17 2008 ]
    Connecting ...connected to 'PLUTO'.
    MQPUT on object 'CHOMPQ.TEST' returned 2053 Queue full..
    mqm@linux-wmq-ams:~>

    This indicates that you need to consider how fast messages will arrive in a queue that is configured for ChompQ. Event queues typically accumulate messages gradually, so you can tune TRIGDEPTH and QDEPTHHI close to MAXDEPTH. On the other hand, if messages will be arriving quickly, it might be necessary to raise MAXDEPTH and set TRIGDEPTH and QDEPTHHI to much lower values. The difference between TRIGDEPTH and MAXDEPTH should be large enough to hold about 1-2 seconds worth of messages. That said, ChompQ.pl cannot keep up with extremely fast message rates such as that which the Q program is capable of producing. But if you had that problem, something like ChompQ is probably not the right solution anyway.


Troubleshooting

The ChompQ.log file

The TESTING constant has been set to 1 in the code listing provided. This means that diagnostics will print for each invocation of ChompQ.pl. Recall that the process object definition contained the command line that executes ChompQ.pl and that the output was directed to ChompQ.log in the WebSphere MQ exits directory. The log should now contain several entries from your testing. Let's examine a typical log entry (Listing 17).

Listing 17
============================================================
/var/mqm/exits/ChompQ.pl: Tue Feb  8 21:23:51 2011

Each entry starts with the eye-catcher line and a time stamp shown in Listing 17.

Listing 18
ARGV = 1.  Values = TMC    2CHOMPQ.TEST
CHOMPQ
/var/mqm/exits/ChompQ.pl >>/var/mqm/exits/ChompQ.log
2>&1
PLUTO
Parm='TMC    2CHOMPQ.TEST
CHOMPQ
/var/mqm/exits/ChompQ.pl >>/var/mqm/exits/ChompQ.log
2>&1
PLUTO                              '

The next line displays the number of arguments passed and their values. As you can see, the trigger message is passed as a single value that contains several recognizable fields such as the program, queue manager, and queue names, as well as the command line. The program anticipates the possibility of multiple parameters passed and prints each one as it is parsed. That is why the trigger message is displayed twice (Listing 18).

Listing 19
         $0 = ChompQ.pl v3
     ApplId = /var/mqm/exits/ChompQ.pl >>/var/mqm/exits/ChompQ.log
   ApplType =
    EnvData =  2>&1
ProcessName = CHOMPQ
   QMgrName = PLUTO
      QName = CHOMPQ.TEST
    StrucId = TMC
TriggerData =
   UserData =
    Version =    2

When a trigger message is found, ChompQ.pl parses all the fields. If debugging is enabled, they are sorted and printed. This helps verify that the process definition was created correctly. If ChompQ.pl runs but does not work, check here to see what values are being passed (Listing 19).

Listing 20
MQSC Command = echo "dis ql('CHOMPQ.TEST') CURDEPTH MAXDEPTH QDEPTHHI" | runmqsc 
	PLUTO 2>&1
5724-H72 (C) Copyright IBM Corp. 1994, 2009.  ALL RIGHTS RESERVED.
Starting MQSC for queue manager PLUTO.
	

     1 : dis ql('CHOMPQ.TEST') CURDEPTH MAXDEPTH QDEPTHHI
AMQ8409: Display Queue details.
   QUEUE(CHOMPQ.TEST)                      TYPE(QLOCAL)
   CURDEPTH(950)                           MAXDEPTH(1000)
   QDEPTHHI(80)
One MQSC command read.
No commands have a syntax error.
All valid MQSC commands were processed.


DELETE = q -dn -q -ICHOMPQ.TEST -L150 -mPLUTO -=0  2>&1
RESPONSE = MQSeries Q Program by Paul Clarke [ V5.0.0 Build:Jul 17 2008 ]
Connecting ...connected to 'PLUTO'.

Next, you see where ChompQ.pl has queried the queue attributes and calculated the number of messages to delete (Listing 20). The -L150 command line argument denotes that 150 messages are to be deleted. For the other parameters, refer to the README file in SupportPac MA01.

Listing 21
5724-H72 (C) Copyright IBM Corp. 1994, 2009.  ALL RIGHTS RESERVED.
Starting MQSC for queue manager PLUTO.
	
     1 : alter ql('CHOMPQ.TEST') trigger
AMQ8008: WebSphere MQ queue changed.
One MQSC command read.
No commands have a syntax error.
All valid MQSC commands were processed.

mqm@linux-wmq-ams:~/exits>

Finally, the log file shows that ChompQ.pl successfully enabled triggering on the queue. As noted earlier, the queue manager disables triggering when a trigger is fired, based on queue depth. If ChompQ.pl did not re-enable triggering on the queue, it would be triggered once only.

If ChompQ.pl does not run

There are many reasons why ChompQ.pl might not run. The main reason on a UNIX or Linux system is that the program was transferred in binary mode. This leaves Windows-style line-ends in place and the Perl interpreter does not like them. Another common problem is that the script is not marked as executable, causing a permission denied error to be returned to the trigger monitor. An incorrect process definition can cause the same symptoms. The result of any of these errors is that the trigger monitor will fire but display an error similar to the following:

Error '32256' starting triggered application (errno 0)

Because a trigger monitor can serve many queues, it needs to be resilient and will continue to run despite this error. The most obvious sign of trouble when ChompQ.pl does not work is that the queue will remain set to NOTRIGGER. If you enable triggering on the queue, a trigger immediately fires and the queue is reset back to NOTRIGGER again. If this happens, verify that the process definition is correct, then make sure that ChompQ.pl will execute from the command line.

Disk free space slowly disappears

ChompQ.pl makes minimal log entries when debugging is disabled but rather lengthy entries otherwise. If you find that you are chronically low on free space in the disk partition that contains the exits directory, verify that debugging has been disabled. Do this by setting the TESTING constant to zero:

use constant TESTING => 0;

This will suppress almost all diagnostics produced by the script.


Post-installation tasks

When you have the code working, there a couple of tasks remaining. The first is to make sure that the diagnostic messages are suppressed. Edit the TESTING constant to contain a value of 0 instead of 1.

The next item is to set up the trigger monitor to run as a service. This will ensure that the trigger monitor is started when the queue manager boots and stopped when the queue manager shuts down. Define the service with the command shown in Listing 22.

Listing 22
DEFINE SERVICE(CHOMPQTRM) +
       CONTROL(QMGR) +
       SERVTYPE(SERVER) +
       STARTCMD('+MQ_INSTALL_PATH+bin/runmqtrm') +
       STARTARG('-m +QMNAME+ -q CHOMPQ.INIT') +
       STOPCMD('+MQ_INSTALL_PATH+bin/amqsstop') +
       STOPARG('-m +QMNAME+ -p +MQ_SERVER_PID+')

Notice that the object definition contains several substitution parameters such as +QMNAME+. This is intentional, do not change these. The queue manager will fill in the correct values at run time for you. This enables the same definition to work across platforms.


Conclusion

The idea of a circular queue that never fills up is useful in many ways. I have already mentioned event queues. Here are a few other use cases:

  • Subscription monitoring. During development of a publish/subscribe application, it is often convenient to create an administrative subscription that dumps messages to a queue for inspection. It is equally inconvenient if that subscription queue fills up.
  • Exception queues. In non-production environments, exception queues are often cleared when they get full so that the application does not stop. A circular queue eliminates both the outages and the requirement to manually manage the queue but also leaves enough messages in the queue to be useful for debugging.
  • Managed durable subscriptions. The default MAXDEPTH for a managed durable subscription queue is 999,999,999. If the subscriber application fails to consume messages, they can build up and consume all available disk space. In non-production environments, it might be convenient to configure ChompQ on the model subscription queue so that orphaned managed subscriptions are capped at a reasonable MAXDEPTH until they can be identified and deleted.

Be aware that all of these use cases mentioned non-production environments. It is usually not a good idea to routinely discard messages in production environments. The alternative is to use a monitoring agent to raise an alarm when queues get too deep or disk space begins to run low.

Having said that, circular queues can make life much easier for developers, testers, and administrators working in the pre-production environments. Experiment, have some fun, and invent your own ways to apply the ChompQ concept.


Download

DescriptionNameSize
Code sampleChompQsample.zip3KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=643639
ArticleTitle=Mission:Messaging: Easing administration and debugging with circular queues
publish-date=03302011