Handling poison messages in IBM MQ classes for JMS

A poison message is one which cannot be processed by a receiving application. If a poison message is delivered to an application and rolled back a specified number of times, the IBM® MQ classes for JMS can move it to a backout queue.

A poison message is a message that cannot be processed by a receiving application. The message could have an unexpected type, or contain information that cannot be handled by the application’s logic. If a poison message is delivered to an application, the application will be unable to process it and will roll it back to the queue where it came from. By default, the IBM MQ classes for JMS will repeatedly redeliver the message to the application. This can result in the application getting stuck in a loop continually trying to process the poison message and rolling it back.

To prevent this from happening, the IBM MQ classes for JMS can detect poison messages, and move them to an alternative destination. To do this, the IBM MQ classes for JMS make use of the following properties:
  • The value of the BackoutCount field within the MQMD of the message that has been detected.
  • The IBM MQ queue attributes BOTHRESH (backout threshold) and BOQNAME (backout requeue queue) for the input queue containing the message.

Whenever a message is rolled back by an application, the queue manager automatically increments the value of the BackoutCount field for the message.

When the IBM MQ classes for JMS detect a message that has a BackoutCount greater than zero, they compare the value of the BackoutCount to the value of the BOTHRESH attribute.
  • If the BackoutCount is less than the value of the BOTHRESH attribute, the IBM MQ classes for JMS deliver it to the application for processing.
  • However, if the BackoutCount is greater than or equal to BOTHRESH, then the message is considered to be a poison message. In this situation, the IBM MQ classes for JMS then move the message to the queue specified by the BOQNAME attribute. If the message cannot be put to the backout queue, then it is either moved to the queue manager’s dead letter queue or discarded, depending upon the report options of the message.
Note:
  • If the BOTHRESH attribute is left at its default value of 0, then poison message handling is disabled. This means that any poison messages are put back to the input queue.
  • The other thing to note is that IBM MQ classes for JMS query the BOTHRESH and BOQNAME attributes for the queue the first time they detect a message that has a BackoutCount greater than zero. The values of these attributes are then cached, and used whenever the IBM MQ classes for JMS encounter a message that has a BackoutCount greater than zero.

Configuring your system to perform poison message handling

The queue that the IBM MQ classes for JMS use when inquiring the BOTHRESH and BOQNAME attributes depends on the style of messaging being performed:
  • For point-to-point messaging, this is the underlying local queue. This is important when a JMS application is consuming messages from either alias queues or cluster queues.
  • For publish/subscribe messaging, a managed queue is created to hold the messages for an application. The IBM MQ classes for JMS query the managed queue to determine the values for the BOTHRESH and BOQNAME attributes.
    The managed queue is created from a model queue associated with the Topic object that the application has subscribed to, and inherits the values of the BOTHRESH and BOQNAME attributes from the model queue. The model queue that is used depends on whether the receiving application has taken out a durable or non-durable subscription:
    • The model queue used for durable subscriptions is specified by the MDURMDL attribute of the Topic. The default value of this attribute is SYSTEM.DURABLE.MODEL.QUEUE.
    • For non-durable subscriptions, the model queue that is used is specified by the MNDURMDL attribute. The default value of the MNDURMDL attribute is SYSTEM.NDURABLE.MODEL.QUEUE.
When inquiring the BOTHRESH and BOQNAME attributes, the IBM MQ classes for JMS:
  • Open the local queue, or the target queue for an alias queue.
  • Inquire the BOTHRESH and BOQNAME attributes.
  • Close the local queue, or the target queue for an alias queue.
The open options that are used when opening the local queue, or the target queue for an alias queue, depend on the version of the IBM MQ classes for JMS being used:
  • For IBM MQ classes for JMS for IBM MQ 9.1.0 Fix Pack 1 and earlier, or IBM MQ 9.1.1, if the local queue, or the target queue for an alias queue, is a cluster queue, then the IBM MQ classes for JMS open the queue with the MQOO_INPUT_AS_Q_DEF, MQOO_INQUIRE and MQOO_FAIL_IF_QUIESCING options. This means that the user running the receiving application must have inquire and get access to the local instance of the cluster queue.

    The IBM MQ classes for JMS open all other types of local queue with the open options MQOO_INQUIRE and MQOO_FAIL_IF_QUIESCING. In order for the IBM MQ classes for JMS to query the values of the attributes, the user running the receiving application must have inquire access on the local queue.

  • [V9.1.0.2 Apr 2019][V9.1.2 Mar 2019]When using the IBM MQ classes for JMS for IBM MQ 9.1.0 Fix Pack 2 and later, or for IBM MQ 9.1.2 and later, the user running the receiving application must have inquire access on the local queue, regardless of the type of the queue.

To move poison messages to either a backout requeue queue or the queue manager’s dead letter queue, you must grant the user running the application put and passall authorities.

Processing poison messages for synchronous applications

If an application receives messages synchronously, by calling one of the following methods, the IBM MQ classes for JMS requeue a poison message within the unit of work that was active when the application tried to get the message:

  • JMSConsumer.receive()
  • JMSConsumer.receive(long timeout)
  • JMSConsumer.receiveBody(Class<T> c)
  • JMSConsumer.receiveBody(Class<T> c, long timeout)
  • JMSConsumer.receiveBodyNoWait Class<T> c)
  • JMSConsumer.receiveNoWait()
  • MessageConsumer.receive()
  • MessageConsumer.receive(long timeout)
  • MessageConsumer.receiveNoWait()
  • QueueReceiver.receive()
  • QueueReceiver.receive(long timeout)
  • QueueReceiver.receiveNoWait()
  • TopicSubscriber.receive()
  • TopicSubscriber.receive(long timeout)
  • TopicSubscriber.receiveNoWait()

This means that if the application is using either a transacted JMS context or session, then the moving of the message to the backout queue is not committed until the transaction is committed.

If the BOTHRESH attribute is set to a value other than zero, the BOQNAME attribute should also be set. If the BOTHRESH is set to a value greater than zero, and the BOQNAME has not been set, the behavior is determined by the report options of the message:
  • If the message has the report option MQRO_DISCARD_MSG set, the message is discarded.
  • If the message has the report option MQRO_DEAD_LETTER_Q specified, then the IBM MQ classes for JMS try to move the message to the queue manager’s dead letter queue.
  • If the message does not have either the MQRO_DISCARD_MSG or MQRO_DEAD_LETTER_Q set, the IBM MQ classes for JMS try to put the message to the dead letter queue for the queue manager.
In the event that the attempt to put the message to the dead letter queue fails for some reason, what happens to the message is determined by whether the receiving application is using a transacted or non-transacted JMS context or session:
  • If the receiving application is using either a transacted JMS context or session, and the transaction is committed, then the message is discarded.
  • If the receiving application is using a transacted JMS context or session, and rolls the transaction back, the message is returned to the input queue.
  • If the receiving application has created a non-transacted JMS context or session, the message is discarded.

Processing poison messages for asynchronous applications

If an application is receiving messages asynchronously via a MessageListener, the IBM MQ classes for JMS requeue poison messages without affecting message delivery. The requeue process takes place outside of any unit of work associated with actual message delivery to the application.

If the BOTHRESH is set to a value greater than zero, and the BOQNAME has not been set, the behavior is determined by the report options of the message:
  • If the message has the report option MQRO_DISCARD_MSG set, the message is discarded.
  • If the message has the report option MQRO_DEAD_LETTER_Q specified, then the IBM MQ classes for JMS try to move the message to the queue manager’s dead letter queue.
  • If the message does not have either the MQRO_DISCARD_MSG or MQRO_DEAD_LETTER_Q set, the IBM MQ classes for JMS try to put the message to the dead letter queue for the queue manager.

If the attempt to put the message to the dead letter queue fails for some reason, the IBM MQ classes for JMS returns the message to the input queue.

For information on how activation specifications and ConnectionConsumers handle poison messages, see Removing messages from the queue in ASF.

What happens to a message when it is moved to the backout queue

When a poison message is requeued to the backout requeue queue, the IBM MQ classes for JMS add an RFH2 header to it (if it did not have one already), and update some of the fields within the message descriptor (MQMD).

If the poison message contains an RFH2 header (because it was a JMS message, for example), the IBM MQ classes for JMS change the following fields within the MQMD when moving the message to the backout requeue queue:
  • The BackoutCount field is reset to zero.
  • The Expiry field of the message is updated to reflect the remaining expiry at the time the poison message was received by the JMS application.
If the poison message does not contain an RFH2 header, the IBM MQ classes for JMS add one and update the following fields in the MQMD as part of the backout processing:
  • The BackoutCount field is reset to zero.
  • The Expiry field of the message is updated to reflect the remaining expiry at the time the poison message was received by the JMS application.
  • The Format field of the message is changed to MQHRF2.
  • The CCSID field is changed to be 1208.
  • The Encoding field is modified to be 273.

In addition to this, the CCSID and Encoding fields from the poison message are copied into the CCSID and Encoding fields of the RFH2 header, to ensure that the header chaining of the message on the backout requeue queue is correct.