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)

Reply via email to