shashank created CAMEL-26634:
--------------------------------
Summary: camel-core - Resequence EIP: batch mode can run two
senders after a route restart
Key: CAMEL-26634
URL: https://issues.apache.org/jira/browse/CAMEL-26634
Project: Camel
Issue Type: Bug
Components: camel-core
Reporter: shashank
Assignee: shashank
Found with Fray (controlled concurrency testing for the JVM): a test that sends
to a batch resequencer while its route is stopped and started logged
{{java.util.ConcurrentModificationException}} at
{{Resequencer$BatchSender.sendExchanges}} (the {{TreeSet}} iterator) in 2 of 60
schedules.
Since CAMEL-20435 every start creates a new {{BatchSender}} thread, and
{{doStop}} only interrupts the old one ({{cancel()}} = {{interrupt()}}). The
stop does not wait for the exchange the old sender is delivering (the
resequencer completes the incoming exchanges when it queues them, so the route
has nothing inflight). If the route is started again before the old sender
finishes that exchange, the old sender goes on: {{while (isRunAllowed())}} is
true again, so it continues with its batch and its next rounds (it only ends at
the interruptible {{await}}, and only while its interrupt status is still set).
Both senders then iterate and remove from the same {{collection}} (a
{{TreeSet}}, shared by all senders):
- the new sender delivers the rest of the old batch while the old sender is
still delivering an earlier exchange, so the exchanges are no longer delivered
one at a time in order (a test sees 2, 3, 4 delivered before 1 is done);
- the old sender's iterator then fails with {{ConcurrentModificationException}};
- when the downstream processing clears the interrupt status (some libraries
swallow it), the old sender never ends: two senders run on the same collection
until the route is stopped again, and every such restart can add one more.
Reproducer (plain JUnit, latches, no sleep):
{{batch().size(2).timeout(60000)}}; the processor of message 1 waits; meanwhile
2 waits in the collection; stop and start the route; send 3 and 4. On main the
mock gets 2, 3, 4 before 1; with a processor that clears the interrupt status,
two "Batch Sender" threads stay alive.
Ways to get there: any stop + start of the route (JMX, dev console, control
bus, CLI, route policies) while the resequencer delivers a slow exchange.
Expected: one sender at a time, so the exchanges are sent in order also across
a restart, and the old sender ends.
Suggested fix: a {{cancelled}} flag next to the interrupt (checked by the
sender loop), and the sender of a new start waits for the previous sender to
end before it sends anything (the start itself does not wait).
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)