[
https://issues.apache.org/jira/browse/CAMEL-24141?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen closed CAMEL-24141.
-------------------------------
Resolution: Fixed
> camel-core - Aggregate EIP recovery and completionInterval background tasks
> are permanently cancelled by the first exception
> ----------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-24141
> URL: https://issues.apache.org/jira/browse/CAMEL-24141
> Project: Camel
> Issue Type: Bug
> Components: camel-core, eip
> Affects Versions: 4.21.0
> Reporter: Federico Mariani
> Assignee: Claus Ibsen
> Priority: Major
> Fix For: 4.22.0
>
> Attachments: AggregateIntervalTaskDiesOnExceptionIssueTest.java,
> AggregateRecoverTaskDiesOnExceptionIssueTest.java
>
>
> Two of the Aggregate EIP's periodic background tasks are scheduled directly
> on a {{ScheduledExecutorService}} without any exception guard. Per
> {{scheduleWithFixedDelay}}/{{scheduleAtFixedRate}} semantics, the first
> exception that escapes {{run()}} *permanently cancels all subsequent
> executions*:
> # *RecoverTask* ({{AggregateProcessor.java:1449-1557}}, scheduled at :1635):
> {{recoverable.scan(camelContext)}}, {{recover(...)}}, {{confirm(...)}} and
> {{onSubmitCompletion(...)}} are all outside any try/catch (only the
> dead-letter {{send}} is wrapped). A single transient failure — e.g. a
> momentary DB outage while {{JdbcAggregationRepository.scan()}} runs —
> permanently disables recovery until the route is restarted, with nothing but
> a one-off stack trace in the logs. For persistent aggregation this means
> completed-but-unconfirmed exchanges are never redelivered: effective data
> loss.
> # *AggregationIntervalTask* ({{AggregateProcessor.java:1384-1437}}, scheduled
> at :1666): {{aggregationRepository.getKeys()}} is called outside any
> try/catch, and inside the loop only {{OptimisticLockingException}} is caught.
> Any other repository/strategy exception kills the fixed-rate task forever —
> the aggregator keeps accepting and aggregating messages but *never completes
> any group by completionInterval*: unbounded memory growth with a memory
> repository, indefinitely stuck groups with a persistent one.
> The project already knows this failure mode:
> {{DefaultTimeoutMap.purgeTask()}} ({{DefaultTimeoutMap.java:163-176}})
> catches all exceptions with the comment _"must catch and log exception
> otherwise the executor will not schedule next purgeTask"_ — which is why the
> completionTimeout checker survives repository hiccups but recovery and
> interval completion do not.
> Related robustness issue on the same path: {{aggregateCompletionCounter}}
> ({{AggregateProcessor.java:924-952}}) does {{switch (completedBy)}} which
> NPEs when the {{CamelAggregatedCompletedBy}} property is absent (e.g. a
> recovered exchange from a repository that did not persist that property) — on
> the recovery path that NPE is itself a task-killer via the missing guard
> above. A null-safe default branch would fix it.
> *Reproducers (attached, both fail on main):*
> * {{AggregateRecoverTaskDiesOnExceptionIssueTest}} — recoverable repository
> whose {{scan()}} throws once; the pending unconfirmed exchange is never
> recovered afterwards.
> * {{AggregateIntervalTaskDiesOnExceptionIssueTest}} — repository whose
> {{getKeys()}} throws once; a group aggregated afterwards is never completed
> by interval.
> Suggested fix: wrap the body of both tasks in try/catch that routes to the
> aggregator's {{ExceptionHandler}} (same pattern as
> {{DefaultTimeoutMap.purgeTask}}), and make {{aggregateCompletionCounter}}
> null-safe.
> _Filed by Claude Code on behalf of Federico Mariani (fmariani)_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)