shashank created CAMEL-25855:
--------------------------------

             Summary: camel-quickfix - an InOut request that times out or 
cannot be sent leaves its correlation rule and exchange in the engine for good
                 Key: CAMEL-25855
                 URL: https://issues.apache.org/jira/browse/CAMEL-25855
             Project: Camel
          Issue Type: Bug
          Components: camel-quickfix
            Reporter: shashank
            Assignee: shashank


For an InOut exchange, {{QuickfixjProducer.sendMessage}} registers a 
correlation rule (the exchange and its {{CorrelationCriteria}}) in the 
{{MessageCorrelator}} of the engine before it sends the FIX message, then waits 
for the reply up to {{CorrelationTimeout}} (default 1000 ms). 
{{MessageCorrelator}} removes a rule only in {{onEvent}}, when a message that 
matches its criteria arrives:

{code:java}
for (MessageCorrelationRule rule : rules) {
    if (rule.getMessageCriteria().evaluate(message)) {
        rule.setReplyMessage(message);
        rules.remove(rule);
        rule.getLatch().countDown();
    }
}
{code}

So a rule stays registered for good, holding its exchange, when:
* the wait times out and no matching message ever arrives (the counterparty 
answers with a session level Reject or a BusinessMessageReject that the 
criteria do not match, or does not answer at all);
* the message is not sent: {{Session.send}} returns false (session not logged 
on, the producer throws {{CannotSendException}}) or throws.

The rules are kept in a {{CopyOnWriteArrayList}} of the engine, which lives as 
long as the component, so they add up for every such request (a route that 
retries InOut sends while the session is down adds one per attempt). Each add 
copies the whole list, and every FIX message the engine receives or sends 
(application and admin messages, heartbeats) is evaluated against every leaked 
rule on the session thread.

*Fix*: the waiting {{Callable}} removes its rule in a finally block (reply, 
timeout or interrupt), and the producer removes the rule of an exchange whose 
message was not sent (new {{MessageCorrelator.cancelReply(Exchange)}}). The 
rule is still registered before the send, so a fast reply is not missed.

*Tests*: new {{QuickfixjProducerCorrelationCleanupTest}} (3 tests, same mocked 
session as {{QuickfixjProducerTest}}). On main: 
{{timedOutRequestsDoNotStayRegistered}} (50 InOut requests with a 10 ms timeout 
and no reply) fails with {{correlation rules left after 50 timed out requests 
==> expected: <0> but was: <50>}}; 
{{requestThatCannotBeSentDoesNotStayRegistered}} and 
{{requestWhoseSendThrowsDoesNotStayRegistered}} fail with {{expected: <0> but 
was: <1>}}. All pass with the fix. camel-quickfix unit tests with the fix: 46 
run, 0 failures (including the existing InOut tests of 
{{QuickfixjProducerTest}}).

*How it was found*: a review of request/reply correlation in producers (where a 
waiting request is registered and where it is removed again). The JUnit tests 
above are the evidence; it was not run against a FIX counterparty.

*Affected*: main, camel-4.23.x, camel-4.22.x, camel-4.18.x and camel-4.14.x 
(same {{MessageCorrelator}} and {{QuickfixjProducer}}; read, not run).

_Filed with Claude Code on behalf of allthingssecurity._




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

Reply via email to