jms-spec
  1. jms-spec
  2. JMS_SPEC-36

Allow messages to be delivered asynchronously in batches

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 1.1
    • Fix Version/s: None
    • Labels:
      None

      Description

      It is proposed that the JMS API be extended to allow messages to be delivered to a MessageListener in batches rather than individually as in JMS 1.1.

      Currently, messages are delivered asynchronously by calling the javax.jms.MessageListener method onMessage(Message message).

      However some applications could process messages more efficiently if they were delivered in batches. Applications would define a new listener class javax.jms.BatchMessageListener with a callback method onMessages(Message[] messages).

      It would be necessary to configure this by adding a new method to javax.jms.MessageConsumer:

      void setBatchMessageListener(BatchMessageListener listener, int batchSize, long batchTimeOut)

      Sets the message consumer's batch message listener. Attempting to set both a message listener and a batch message listener on the same MessageConsumer would cause a javax.jms.IllegalStateException.

      • listener The BatchMessageListener being set
      • batchSize If set to a value greater than zero, messages will be delivered in batches of up to batchSize messages. The actual batch size used may be smaller than this, but it may never be larger.
      • batchTimeOut The maximum number of milliseconds that the JMS provider may defer message delivery for in order to assemble a batch of messages that is as large as possible but no larger than the batch size.

      Acknowledgement:

      • If auto-acknowledgement was being used, all messages in the batch would be acknowledged together. Section 4.4.12 "Duplicate Delivery of Messages" of the JMS 1.1 spec explains that when auto-acknowledgement is being used, if a failure occurs, clients can't know for sure whether the message has been successfully acknowledged, and so the last consumed message may may be redelivered. This section would need to be extended to state that in the case of batch delivery, if a failure occurs, all messages in the last batch may be redelivered.
      • If dups-ok acknowledgement was being used, message acknowledgement would follow existing semantics.
      • If client acknowledgement, or local or global transactions, were being used then message acknowledgement would follow existing semantics, which is that a call to acknowledge(), or the commit of the transaction, would acknowledge all unacknowledged messages delivered by the session.

        Issue Links

          Activity

          Hide
          Nigel Deakin added a comment -

          Updated description to specify that a new kind of listener would be used BatchMessageListener which was set by calling setBatchMessageListener(listener, batchSize, batchTimeout).

          Show
          Nigel Deakin added a comment - Updated description to specify that a new kind of listener would be used BatchMessageListener which was set by calling setBatchMessageListener(listener, batchSize, batchTimeout) .
          Hide
          Nigel Deakin added a comment -

          Corrected formatting errors and typos above.

          I considered whether there should be a method setbatchMessageListener(BatchMessageListener listener) which used values of batch timeout and batch size that were set using new setter methods setBatchTimeout and setBatchSize.

          I decided that decided that this was not appropriate since

          • you can only set a single listener on a given MessageConsumer so setting the batch timeout and batch size on the MessageConsumer does not avoid any repetition of values.
          • there is no sensible default batch size or batch timeout. Offering a default batch size of 1 is safe but unhelpful, and there is no sensible default batch timeout. So it is appropriate and reasonable to expect the application to set these in the call to setBatchMessageListener(BatchMessageListener listener, int batchSize, long batchTimeOut).
          Show
          Nigel Deakin added a comment - Corrected formatting errors and typos above. I considered whether there should be a method setbatchMessageListener(BatchMessageListener listener) which used values of batch timeout and batch size that were set using new setter methods setBatchTimeout and setBatchSize . I decided that decided that this was not appropriate since you can only set a single listener on a given MessageConsumer so setting the batch timeout and batch size on the MessageConsumer does not avoid any repetition of values. there is no sensible default batch size or batch timeout. Offering a default batch size of 1 is safe but unhelpful, and there is no sensible default batch timeout. So it is appropriate and reasonable to expect the application to set these in the call to setBatchMessageListener(BatchMessageListener listener, int batchSize, long batchTimeOut) .
          Hide
          Nigel Deakin added a comment -

          In order to allow the existing "Chapter 8 API" to use the BatchMessageListener interface it will be necessary to add a new method to javax.jms.Session:

          void setBatchMessageListener(BatchMessageListener listener, int batchSize, long batchTimeOut)

          This would be optional just as the existing setMessageListener method is.

          Show
          Nigel Deakin added a comment - In order to allow the existing "Chapter 8 API" to use the BatchMessageListener interface it will be necessary to add a new method to javax.jms.Session : void setBatchMessageListener(BatchMessageListener listener, int batchSize, long batchTimeOut) This would be optional just as the existing setMessageListener method is.
          Hide
          Nigel Deakin added a comment - - edited

          I've now updated the javadocs and the draft spec with details of this new feature (these changes are additive, so those docs include other changes).

          The updated Javadocs are here:
          http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar
          (See two new methods on MessageProducer, two new methods on MessagingContext, and a new interface CompletionListener)

          The updated draft spec is here:
          http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf
          (All changes are highlighted clearly with changebars. The main changes now are a new BatchMessageListener interface and new methods on MessageConsumer and MessagingContext. I've also added new methods to Session for use with the ConnectionConsumer API)

          Show
          Nigel Deakin added a comment - - edited I've now updated the javadocs and the draft spec with details of this new feature (these changes are additive, so those docs include other changes). The updated Javadocs are here: http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar (See two new methods on MessageProducer, two new methods on MessagingContext, and a new interface CompletionListener) The updated draft spec is here: http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf (All changes are highlighted clearly with changebars. The main changes now are a new BatchMessageListener interface and new methods on MessageConsumer and MessagingContext. I've also added new methods to Session for use with the ConnectionConsumer API)
          Hide
          colincrist added a comment -

          I'd like to add a comment on a specific use case I've seen from time to time with queues and durable subscriptions - mostly the latter - and I think it probably fits here.

          Consider a service (MDB or plain JMS) starting up and there are a bunch of messages on a queue or durable subscription for it to process. The simple way is to begin processing then one by one.

          However this can be quite inefficient if the processing for each message is time consuming and there is a big backlog of messages. Often there are many changes in the batch for the same entity (e.g. order/trade etc) and earlier versions of the update messages can either be skipped or merged together to result in a single update to each entity for the batch.

          An optimisation is to:

          *) consumer all the messages available (or in large batches) in a transaction [I've seen this done by going behind the JMS API to JMX or a custom API and get the queue depth or durable subscription depth - often requiring different authentication - ikky]
          *) pre-process all the messages coalescing or discarding messages no longer relevant.
          *) process the resulting batch of updates.

          Couple of questions:

          1) Reading the current version of the spec for batch delivery can you confirm that it is the intention that this pattern can be used? If I were to setBatchMessageListener(foo, 1000, 0) and there were X thousand messages pending then I'd then those messages in batches of 1000 and then very small batch message delivery depending on the implementation/message rate etc after that.

          2) There seems to be no matching synchronous Message[] MessageConsumer.receiveBatch(int batchSize, long batchTimeout). Is this intentional and if so what's the rational for not including it?

          Show
          colincrist added a comment - I'd like to add a comment on a specific use case I've seen from time to time with queues and durable subscriptions - mostly the latter - and I think it probably fits here. Consider a service (MDB or plain JMS) starting up and there are a bunch of messages on a queue or durable subscription for it to process. The simple way is to begin processing then one by one. However this can be quite inefficient if the processing for each message is time consuming and there is a big backlog of messages. Often there are many changes in the batch for the same entity (e.g. order/trade etc) and earlier versions of the update messages can either be skipped or merged together to result in a single update to each entity for the batch. An optimisation is to: *) consumer all the messages available (or in large batches) in a transaction [I've seen this done by going behind the JMS API to JMX or a custom API and get the queue depth or durable subscription depth - often requiring different authentication - ikky] *) pre-process all the messages coalescing or discarding messages no longer relevant. *) process the resulting batch of updates. Couple of questions: 1) Reading the current version of the spec for batch delivery can you confirm that it is the intention that this pattern can be used? If I were to setBatchMessageListener(foo, 1000, 0) and there were X thousand messages pending then I'd then those messages in batches of 1000 and then very small batch message delivery depending on the implementation/message rate etc after that. 2) There seems to be no matching synchronous Message[] MessageConsumer.receiveBatch(int batchSize, long batchTimeout). Is this intentional and if so what's the rational for not including it?
          Hide
          Nigel Deakin added a comment -

          @colincrist: Please note that the best place for general discussion is users@jms-spec.java.net

          (1) Calling setBatchMessageListener(foo, 1000, 0) would means that the application was requesting messages to be delivered in batches of no more than 1000. A timeout of 0 means that the JMS provider is allowed to delay message delivery as long as it wanted in order to assemble a batch of the required size. I think a shorter timeout than that would almost always be preferable.

          So if this was called when there were 3500 messages on the queue, three batches of 1000 would be delivered and then the server would wait, potentially for ever, for a fourth batch to be assembled.

          If a smaller tineout were specified (say 1000ms) then the server would wait for 1 second and then deliver a batch of 500.

          The JMS provider is allowed to deliver a smaller batch size than is requested, and to use a shorter timeout than that requested. However the basic idea is that specifying a batch size of 1000 means that they application would prefer batches of 1000 if possible. So although a JMS provider could probably legally offer the behaviour you describe, this would be contrary to what is intended.

          (2) Yes this was intentional, since in this case an application could assemble a batch of 1000 simply by calling receive() 1000 times, which would not be possible in the async case. However I can see merit in allowing batches to be delivered to sync consumers as well. Watch this space...

          Show
          Nigel Deakin added a comment - @colincrist: Please note that the best place for general discussion is users@jms-spec.java.net (1) Calling setBatchMessageListener(foo, 1000, 0) would means that the application was requesting messages to be delivered in batches of no more than 1000. A timeout of 0 means that the JMS provider is allowed to delay message delivery as long as it wanted in order to assemble a batch of the required size. I think a shorter timeout than that would almost always be preferable. So if this was called when there were 3500 messages on the queue, three batches of 1000 would be delivered and then the server would wait, potentially for ever, for a fourth batch to be assembled. If a smaller tineout were specified (say 1000ms) then the server would wait for 1 second and then deliver a batch of 500. The JMS provider is allowed to deliver a smaller batch size than is requested, and to use a shorter timeout than that requested. However the basic idea is that specifying a batch size of 1000 means that they application would prefer batches of 1000 if possible. So although a JMS provider could probably legally offer the behaviour you describe, this would be contrary to what is intended. (2) Yes this was intentional, since in this case an application could assemble a batch of 1000 simply by calling receive() 1000 times, which would not be possible in the async case. However I can see merit in allowing batches to be delivered to sync consumers as well. Watch this space...
          Hide
          Nigel Deakin added a comment -

          Following discussions on the JSR 343 expert group it is clear that there is insufficient support for this change, so I'm removing all references to batch delivery from JMS 2.0.

          There does appear to be some support for looking at this again in the Java EE 8 timescale, when changes to the JCA spec may allow a more generic way of defining async message listeners that does not rely on implementing a specific interface. This would allow the delivery of batches of messages without complicating the API unduly.

          We'll look at this again for JMS 2.1. Tagging accordingly.

          Show
          Nigel Deakin added a comment - Following discussions on the JSR 343 expert group it is clear that there is insufficient support for this change, so I'm removing all references to batch delivery from JMS 2.0. There does appear to be some support for looking at this again in the Java EE 8 timescale, when changes to the JCA spec may allow a more generic way of defining async message listeners that does not rely on implementing a specific interface. This would allow the delivery of batches of messages without complicating the API unduly. We'll look at this again for JMS 2.1. Tagging accordingly.

            People

            • Assignee:
              Unassigned
              Reporter:
              Nigel Deakin
            • Votes:
              4 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated: