Federico Mariani created CAMEL-24074:
----------------------------------------

             Summary: camel-jms: TemporaryQueueReplyManager resolves the 
temporary reply queue without synchronization - replies can be black-holed 
(CAMEL-20769 follow-up)
                 Key: CAMEL-24074
                 URL: https://issues.apache.org/jira/browse/CAMEL-24074
             Project: Camel
          Issue Type: Bug
          Components: camel-jms
    Affects Versions: 4.21.0
            Reporter: Federico Mariani
         Attachments: TemporaryQueueReplyManagerConcurrentResolveTest.java

h3. Problem
{{TemporaryQueueReplyManager.TemporaryReplyQueueDestinationResolver.resolveDestinationName}}
 (TemporaryQueueReplyManager.java:285-315) is a check-then-act on the plain, 
non-volatile {{queue}} field with no synchronization. CAMEL-20769 
(7313a1e53a63, May 2024) deliberately removed the previous {{synchronized 
(refreshWanted)}} block to fix an endless lock during shutdown - but replaced 
it with nothing.

With {{replyToConcurrentConsumers > 1}} (a supported configuration, see 
{{JmsRequestReplyTempQueueMultipleConsumersTest}}), several listener-container 
invoker threads resolve the destination concurrently at startup and after every 
{{scheduleRefresh()}}:

* T1 sees {{queue==null}}, creates Q1; T2 concurrently creates Q2, writes 
{{queue=Q2}}, calls {{setReplyTo(Q2)}}; T1's {{setReplyTo(Q1)}} lands last -> 
{{replyTo=Q1}} while consumers listen on Q2.
* Once T1's cached consumer on Q1 is recycled, every reply is sent to Q1 and 
*black-holed* -> all InOut requests time out until a JMS exception forces a 
refresh.
* A mid-refresh {{scheduleRefresh()}} can also be swallowed 
({{refreshWanted.set(false)}} before the new queue is fully published).

h3. Reproducer
Attached {{TemporaryQueueReplyManagerConcurrentResolveTest.java}} (place in 
{{components/camel-jms/src/test/java/org/apache/camel/component/jms/reply/}}). 
It drives the resolver from two threads with a barrier inside 
{{createTemporaryQueue()}} making the race deterministic: today two temporary 
queues are created (the test expects one). A correctly synchronized resolver 
passes - the barrier simply times out for the single caller. No broker needed.

h3. Note on the fix
The CAMEL-20769 shutdown-deadlock motivation must be preserved - synchronize 
the resolver on its own dedicated lock (the same pattern QueueReplyManager's 
DestinationResolverDelegate uses, with the in-code explanation of why not 
BaseService.lock).

----
_This issue was researched and filed by Claude Code on behalf of [~croway] 
(GitHub: Croway), as part of a deep code review of camel-jms and camel-amqp. A 
failing JUnit reproducer is attached; it fails deterministically on current 
main (4.22.0-SNAPSHOT)._



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to