0

I have two Camel routes, configured in XML and pasted below: -

Route 1:

<camel:route id="statementsArchivingPollRoute">
    <camel:from uri="timer://tempQueue?fixedRate=true&amp;period=30s&amp;delay=30s"/>
    <camel:transacted ref="PROPAGATION_REQUIRED">
    <camel:process ref="statementsArchivingRequestZipProcessor"/>
    <camel:choice>
        <camel:when>
            <camel:simple>${body.size} >= 1</camel:simple>
            <camel:split>
                <camel:simple>${body}</camel:simple>
                <camel:marshal ref="archiveFileInterfaceMetadataMapper"/>
                <camel:to pattern="InOnly"
                          uri="activemq:{{ccs.activemq.queue.prefix}}.sr.archive.bulk.ingestion.req?jmsMessageType=Text"/>
            </camel:split>
            <camel:log loggingLevel="INFO" message="Archiving content was processed"/>
        </camel:when>
        <camel:otherwise>
            <camel:log loggingLevel="INFO" message="No archiving content to process"/>
        </camel:otherwise>
    </camel:choice>
    </camel:transacted>
</camel:route>

Route 2:

<camel:route id="statementsArchivingBulkIngestionRequestRoute">
    <camel:from uri="activemq:{{ccs.activemq.queue.prefix}}.sr.archive.bulk.ingestion.req"/>
    <camel:throttle timePeriodMillis="4000">
    <camel:constant>1</camel:constant>
    <camel:setExchangePattern pattern="InOnly"/>
    <camel:unmarshal ref="archiveFileInterfaceMetadataMapper"/>
    <camel:bean ref="archiveFileEntryTransformer" method="transform"/>
    <camel:setHeader headerName="CamelHttpMethod">
        <camel:constant>POST</camel:constant>
    </camel:setHeader>
    <camel:toD uri="{{ccs.bulk.ingestion.service.ingest.archive.file}}"/>
    </camel:throttle>
</camel:route>

The processor in the first route returns a list of request objects. The list is then split and each request is marshalled and placed on a queue.

The second route listens to this queue. When it de-queues a message, it unmarshals it, performs a transform and then uses it to send a post request to another service. I am throttling this route so that it only processes one message per second so as not to overwhelm the downstream service.

This all works fine when the list only contains a few requests and hence only a few messages enter the queue, but when there are many items in the list, route 2 times out and the log entry below appears:

Atomikos:12] c.a.icatch.imp.ActiveStateHandler        : Timeout/setRollbackOnly of ACTIVE coordinator !

The timeout leads to the process repeating itself and the downstream service ends up getting called multiple times per message instead of just once.

I can't understand why the number of times route 2 is invoked should cause it to timeout. I thought that a single instance of the route would be launched for every message de-queued from the activemq. If a single message takes a long time to complete, then I would understand, but clearly the timeout is based on the cumulative times of all messages being de-queued.

I am fairly new to Camel and I have clearly misunderstood something from an architectural point of view. I would be extremely grateful for any guidance into how to stop these timeouts occurring. Thank you for reading.

Jon H
  • 394
  • 3
  • 17
  • I'm not an expert at Camel transactions, but I think you've got an issue because you have made a transaction that includes everything you do. Normally, when you have a consumer and make it transactional, then the transaction is per exchange received by the consumer. In this instance, your transaction exchange is per timer instance. Does it need to be transactional? Does it work ok if you stop it being transactional? – Screwtape Jan 15 '20 at 15:45
  • @Screwtape This is a good suggestion and I think you are right. I removed the transacted tag and tried again, but I got the following error: "AtomikosTransactionRequiredJMSException: The JMS session you are using requires a JTA transaction context for the calling thread and none was found." So it clearly requires this. So I put it back, but removed the "PROPAGATION_REQUIRED" ref. Unfortunately this gives exactly the same behaviour as the original post! But I definitely think this is the direction to be looking in. Many thanks - I will do some more searching around this. – Jon H Jan 15 '20 at 16:14
  • Hmm... given your first route says "InOnly" on the queue producer, then I wouldn't have expected that you needed to complete everything in the second route to finish the transaction in the first. I note that your timer is set to fixed rate - could it be that you have so many records to write to the queue that you haven't finished the first exchange before the next one starts? Might that be an issue. Otherwise I'm stumped. – Screwtape Jan 15 '20 at 16:40
  • @Screwtape Yes that's what I thought! And no, there are only a few records in the queue, so I'm pretty stumped too. But many thanks for your interest - you have at least given me the direction to look into! :) – Jon H Jan 15 '20 at 16:53
  • Given that 1 works without 2, I think you need to get to grips with the transaction policies. See: https://camel.apache.org/manual/latest/transactional-client.html#TransactionalClient-TransactionPolicies which implies that the issue you're having is exactly as expected with PROPAGATION_REQUIRED, as that extends the parent transaction to the destination consumer. Perhaps you need PROPAGATION_REQUIRES_NEW, or perhaps the not supported, which isn't made clear how to specify from Spring XML. – Screwtape Jan 16 '20 at 14:51
  • @Screwtape This is so useful, thank you. I think this is the root of the problem. Appreciate you coming back to the post :) – Jon H Jan 16 '20 at 16:39

1 Answers1

1

Jon, you may want to try following to investigate

  1. disable/comment the 2nd route. Purpose of using activemq would be to make process async which means 1st route should not have any impact due to 2nd route. If this works without route 2 then problem is elsewhere.

  2. If you find 1st route is working fine without 2nd route then next would be to try setting fewer number of threads in 2nd route. May be put 1 or 2 threads and see if that helps. I am thinking it is contention on activemq rather than these route configurations.

  3. Check the payload size that you are pushing to activemq. If you are publishing very large message to activemq that may also have an impact as large number of items and each item is large causing contention in activemq and transaction is taking longer than the timeout setting.

If you are pushing large data set to activemq, you may want to revisit the design. Save payload in some persistence area (db/file/cache) and sent notification events containing only reference to the payload and some metadata. 2nd route can then take the reference from event and retrieve the payload from where it was saved by route 1.

  • Many thaks for these suggestions. I have tried 1. This has confirmed that the 1st route has no problem without the 2nd route. Regarding 3, the payload size shouldn't be a problem - it's a simple POJO with about 7 properties. I will try 2, but I am not sure how to configure the number of threads for a single route. Could you suggest how? I am thinking that the throttling may be the underlying cause. I am wondering if I can configure the de-queue delay on the queue itself rather than the route. Will continue to investigate! – Jon H Jan 15 '20 at 17:43
  • Now that we know route 1 is okay on its own, you can run route 2 alone. First time comment route 2 so that messages are pushed in queue. Then restart with route 2 on and route 1 commented. If you see timeouts then you have narrowed the scope. Check camel documentation for activemq component and you will find some interesting options to set. Also try removing that throttle. May be that is blocking your thread and causing Atomikos timeouts. It will make sense to put pause/throttle outside ot transaction boundary instead of using within the transaction block. – Anil Hemnani Jan 15 '20 at 20:55
  • These are great suggestions. So I comment route 2 out, and route 1 runs fine. I comment route 1 out, reactivate route 2 and restart. Route 2 starts to dequeue the messages and I am still seeing the timeout messages. So I now know the problem is in route 2, thank you for leading me to that. I feel it must be the throttle. But if i remove the throttle, that will almost certainly lead to problems downstream. I will have to re-think the design or try to find another way to throttle the messages. Any more suggestions very welcome :) – Jon H Jan 16 '20 at 08:53
  • Try moving outside if transaction scope. It should not be inside transact block. Think of some other way – Anil Hemnani Jan 16 '20 at 18:24