Federico Mariani created CAMEL-24142:
----------------------------------------

             Summary: camel-core - Multicast/Split timeout silently lost when 
it fires while a result is being aggregated (tryLock never retried)
                 Key: CAMEL-24142
                 URL: https://issues.apache.org/jira/browse/CAMEL-24142
             Project: Camel
          Issue Type: Bug
          Components: camel-core, eip
    Affects Versions: 4.21.0
            Reporter: Federico Mariani


The Multicast/Split {{timeout}} task is scheduled *once* per multicast 
execution ({{MulticastProcessor.java:452-453}}), and 
{{MulticastTask.timeout()}} starts with {{lock.tryLock()}} 
({{MulticastProcessor.java:507-509}}) — if the lock is not free at the instant 
the timeout fires, the method returns immediately and the timeout is *never 
rescheduled*.

The same lock is held by {{MulticastTask.aggregate()}} for the entire duration 
of each completion's aggregation ({{completion.poll()}} + {{doAggregate}}, 
which runs the user's {{AggregationStrategy}}), and briefly by 
{{AsyncCompletionService.complete/poll}}. So whenever the timeout fires while 
any sub-exchange result is being aggregated, the timeout is silently dropped. 
If a remaining branch/item never replies — the very situation {{timeout}} 
exists for — the EIP never completes and the exchange *hangs forever* despite 
the configured timeout.

The race window is proportional to (aggregation time × number of completions); 
a slow custom aggregation strategy widens it arbitrarily.

*Reproducer (attached, fails on main):* 
{{MulticastTimeoutLostDuringAggregationIssueTest}} makes the race deterministic 
— branch A completes immediately and its aggregation blocks for 2s (holding the 
task lock), the timeout (500ms) fires in that window and is lost, branch B 
stays blocked, and the exchange does not complete within 8s:

{noformat}
org.opentest4j.AssertionFailedError: Configured multicast timeout (500ms) was 
silently lost because it fired while the aggregation lock was held
{noformat}

Suggested fix direction: on failed {{tryLock}}, reschedule {{timeout()}} after 
a short delay; or use {{lock.lock()}} — the aggregate loop's hold time is 
bounded by the strategy invocation, and the timeout task runs on the dedicated 
unbounded aggregate executor so blocking is acceptable there.

_Filed by Claude Code on behalf of Federico Mariani (fmariani)_



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

Reply via email to