CAMEL-24142: Fix Multicast/Split timeout silently lost when fired during aggregation#24822
Conversation
…ing aggregation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
|
🌟 Thank you for your contribution to the Apache Camel project! 🌟 🐫 Apache Camel Committers, please review the following items:
|
gnodet
left a comment
There was a problem hiding this comment.
Review: CAMEL-24142 — Fix Multicast/Split timeout silently lost when fired during aggregation
What works well
Precise diagnosis of the race condition. The timeout() method used tryLock(), which was correct for aggregate() (called repeatedly from the completion queue — safe to skip since the next call retries) but incorrect for timeout() (a one-shot scheduled task — if it fails to acquire the lock, it never retries, and the exchange hangs forever).
Correct fix. Changing to lock.lock() is safe because the timeout task runs on a dedicated scheduled executor thread, not a routing thread. Blocking until the aggregation releases the lock is the right behavior — the timeout will fire slightly late but will fire.
Essential done.get() guard. After acquiring the lock, checking done.get() prevents the timeout from processing if the multicast completed normally while the timeout was waiting. This avoids double-completion.
Well-designed reproducer test. MulticastParallelTimeoutDuringAggregationTest makes the race deterministic: the SlowAggregationStrategy holds the lock for 2 seconds while the 500ms timeout fires mid-aggregation. Before the fix, tryLock() would fail and the exchange would hang. The @Isolated annotation and CountDownLatch coordination are appropriate.
Checklist
| Item | Status |
|---|---|
| Tests | ✅ New reproducer test + 177 existing aggregator tests |
| Docs / Upgrade guide | N/A (bug fix for internal race condition, no API change) |
| Commit convention | ✅ CAMEL-24142: <description> |
| Public API / backward compat | ✅ No API changes |
| Security | N/A |
| CI | ⏳ Pending |
Static analysis: semgrep (0 findings).
Clean, minimal, and correct fix for a race condition that has existed since CAMEL-16103 (2021). No concerns.
Reviewed with Claude Code on behalf of gnodet. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.
oscerd
left a comment
There was a problem hiding this comment.
LGTM — correct race fix. Switching the one-shot timeout() from tryLock() to lock.lock() with a post-acquire done.get() guard means a timeout that races with aggregation no longer silently drops: it now blocks for the per-task lock and, once acquired, either surfaces the timeout or (if completion won) returns via the guard. No deadlock — a single per-task ReentrantLock shared by aggregate()/timeout()/completion, no lock ordering; done is CAS-set under the lock in doDone/doTimeoutDone, and the cancelled timeout thread just acquires after release and returns. Good deterministic reproducer.
Reviewed with Claude Code on behalf of Andrea Cosentino. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.
|
🧪 CI tested the following changed modules:
🔬 Scalpel shadow comparison — Scalpel: 544 tested, 29 compile-only — current: 544 all testedMaveniverse Scalpel detected 573 affected modules (current approach: 544).
|
Summary
MulticastTask.timeout()wheretryLock()silently drops the timeout when the aggregation lock is held, causing the exchange to hang forevertimeout()to uselock.lock()instead oftryLock()— the timeout is a one-shot scheduled task that runs on a dedicated executor, so blocking until the lock is available is safe and correctdone.get()guard after acquiring the lock to skip timeout handling if the multicast already completed normallyThe bug was introduced in CAMEL-16103 (2021) when
tryLock()was correctly applied toaggregate()(called repeatedly, safe to skip) but also incorrectly applied totimeout()(called once, never retried). The fix affects Multicast, Split, and RecipientList EIPs since they all shareMulticastProcessor.Claude Code on behalf of davsclaus
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com