[
https://issues.apache.org/jira/browse/CAMEL-26328?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Work on CAMEL-26328 started by shashank.
----------------------------------------
> camel-core - Failover load balancer: fails over on an exception that is only
> suppressed (such as one thrown in a doFinally) and drops the exception that
> failed the attempt
> ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-26328
> URL: https://issues.apache.org/jira/browse/CAMEL-26328
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Assignee: shashank
> Priority: Minor
>
> The failover load balancer with a list of exceptions fails over when the
> exception that failed the attempt is not in the list but one of its
> *suppressed* exceptions is. Since CAMEL-25114 (4.23.0) an exception thrown in
> a {{doFinally}} block is added as a suppressed exception to the pending
> exception of the {{doTry}} block, so a cleanup step that throws one of the
> listed types now makes the load balancer fail over. When the next processor
> succeeds, the exception that failed the attempt is lost and the route goes on
> (main ffab48ea514b):
> {code:java}
> from("direct:start")
> .loadBalance().failover(IllegalArgumentException.class)
> .to("direct:attempt")
> .to("mock:second")
> .end()
> .to("mock:after");
> from("direct:attempt")
> .doTry()
> .throwException(new IOException("original"))
> .doFinally()
> .throwException(new IllegalArgumentException("cleanup"))
> .end();
> main / 4.23.0: exception=null, mock:second=1, mock:after=1 (failed over on
> the doFinally exception, IOException lost)
> 4.22.1: exception=IOException("original"), no failover
> {code}
> The same happens when the {{doFinally}} calls a route that throws.
> {{onException(IllegalArgumentException.class)}} and
> {{doCatch(IllegalArgumentException.class)}} do not match this IOException:
> they look at the exception and its causes only (doTry-eip.adoc, "How doCatch
> matches an exception"; {{DefaultExceptionPolicyStrategy}} and
> {{CatchProcessor}} walk {{ObjectHelper.createExceptionIterable}}). The
> failover page says the load balancer "can be configured to only apply for a
> specific set of exceptions", and does not mention suppressed exceptions.
> *Cause*: {{FailOverLoadBalancer.shouldFailOver}} checks each listed type with
> {{exchange.getException(type)}}. That goes to
> {{ObjectHelper.getException(Class, Throwable)}}, which looks at the
> suppressed exceptions first, then at the cause chain. That lookup was added
> for CAMEL-6406 (2.16.0) as a general API change, not for failover. Before
> CAMEL-25114 the suppressed exceptions that camel-core added on the routing
> path were mostly the other way round: when an {{onException}} block fails
> while it handles an exception, {{FatalFallbackErrorHandler}} adds the
> original exception as a suppressed exception to the new one (also on
> camel-4.22.x), so there the suppressed exception is the one the attempt first
> failed with. A failing {{onCompletion}} in {{modeBeforeConsumer}} also adds
> its exception as suppressed (camel-4.22.x too). The CAMEL-25114 PR did not
> look at the load balancer.
> *Fix* (proposed): {{shouldFailOver}} matches the listed types against the
> exception and its causes only, with {{ObjectHelper.createExceptionIterable}},
> as {{onException}} and {{doCatch}} do. A paragraph on the failover EIP page
> (and its catalog copy), and a 4.24 upgrade guide entry, because a listed
> exception that is only suppressed no longer causes failover in these cases
> either: an {{onException}} block that fails while it handles a listed
> exception (the exchange fails with the new exception), a {{doCatch}} whose
> {{onWhen}} throws (since CAMEL-25114 the exchange fails with the predicate's
> exception, the original one suppressed), and a processor or component that
> adds one itself. A route that relied on it adds the type of the exception the
> attempt fails with to the list. Not changed:
> {{ThrottlingExceptionRoutePolicy}} checks its {{throttledExceptions}} with
> the same {{exchange.getException(type)}} loop (a follow-up).
> *Tests*: {{FailOverLoadBalanceSuppressedExceptionTest}} (camel-core, 11
> tests). A doFinally that throws a listed exception, a doFinally that calls a
> route that throws one, the same with inheritErrorHandler, round robin and
> maximumFailoverAttempts, a processor that adds one as suppressed, an
> onException block that fails while handling a listed exception, and a doCatch
> whose onWhen throws: no failover, and the exchange fails with the exception
> of the attempt with the listed one as its suppressed exception. These 6 fail
> on main and pass with the fix. Controls that pass on both: a listed exception
> as the cause of an unlisted one fails over, a listed exception thrown
> directly fails over, a listed exception from doTry with an unlisted one from
> doFinally fails over, an unlisted exception does not, and {{onException}}
> does not match the suppressed exception. The load balancer, failover and
> doTry/doCatch/doFinally tests of camel-core pass (196 tests).
> *How it was found*: a Rocq specification of Camel's EIPs written from the
> documentation, compared with real Camel on generated routes (a failover
> monitoring IllegalArgumentException whose attempt's doFinally called a route
> that threw one), then reproduced on HotSpot against 4.22.1 and main.
> *Affected*: the doFinally case: 4.23.0 and main (camel-4.22.x does not add
> the doFinally exception as suppressed; 4.22.1 checked). A processor or
> component that adds a suppressed exception itself: every version since
> 2.16.0. A failing {{onCompletion}} in {{modeBeforeConsumer}}: 4.0.0 and later
> (checked on the 4.0.0 tag).
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)