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)