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)