[ 
https://issues.apache.org/jira/browse/CAMEL-24136?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18096942#comment-18096942
 ] 

Claus Ibsen commented on CAMEL-24136:
-------------------------------------

Fixed via https://github.com/apache/camel/pull/24800

> Circuit Breaker EIP - umbrella for medium findings (FT exchange-property 
> contract, parity gaps, dead option, docs)
> ------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-24136
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24136
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-microprofile-fault-tolerance, eip
>    Affects Versions: 4.21.0
>            Reporter: Federico Mariani
>            Assignee: Claus Ibsen
>            Priority: Major
>             Fix For: 4.22.0
>
>         Attachments: camel-circuitbreaker-eip-review-2026-07.md
>
>
> Umbrella for the medium-severity findings from a review of the Circuit 
> Breaker EIP (core model, camel-resilience4j, 
> camel-microprofile-fault-tolerance). Full review document attached. Related 
> bugs filed separately: CAMEL-24133 (pooled-mode exchange corruption), 
> CAMEL-24134 (timeout write-back race), CAMEL-24135 (JMX metric).
> h3. M1. FT: no-fallback timeout/short-circuit paths never set the documented 
> exchange properties
> When there is no fallback, a guard exception lands in 
> {{FaultToleranceProcessor.process()}} {{catch (Exception e) -> 
> exchange.setException(e)}}; {{CamelCircuitBreakerResponseTimedOut}} and 
> {{...SuccessfulExecution=false}} are never set. The branch that would set 
> them (inside {{CircuitBreakerFallbackTask}}, the {{fallbackProcessor == 
> null}} block) is unreachable dead code, because the fallback task is only 
> acquired when a fallback exists. The resilience4j implementation sets these 
> properties in the same scenario. No test in either module asserts 
> {{RESPONSE_TIMED_OUT}} at all.
> h3. M2. FT: guard-level exceptions invisible to the fallback 
> ({{CamelExceptionCaught}} = null)
> Guard-raised exceptions (SmallRye {{TimeoutException}}, 
> {{CircuitBreakerOpenException}}, bulkhead rejection) are never stored on the 
> exchange before the fallback runs, so the fallback route sees 
> {{exchange.getException() == null}} and {{EXCEPTION_CAUGHT}} is set to null. 
> Only exceptions thrown by the protected processor itself are visible. In the 
> resilience4j implementation the throwable is always available to the recover 
> function. Fallback routes that branch on the cause behave differently between 
> the two implementations.
> h3. M3. FT feature-parity gaps vs the resilience4j implementation
> * no {{recordExceptions}} / {{ignoreExceptions}} equivalent - SmallRye's 
> circuit breaker builder supports {{failOn()}} / {{skipOn()}}, so this is 
> wireable; {{CamelCircuitBreakerResponseIgnored}} is never set by FT;
> * no {{throwExceptionWhenHalfOpenOrOpenState}} equivalent (open circuit + no 
> fallback always silently continues routing);
> * live metrics: FT JMX exposes only configuration echoes; {{getFailureRate}} 
> is described as "the current failure rate in percentage" but returns the 
> *configured* ratio as a 0-1 fraction. The FT dev console is likewise 
> config-only, while the resilience4j console shows live state and counters.
> h3. M4. FT: {{timeoutPoolSize}} option is dead
> Parsed by the reifier and echoed in JMX, but never applied to the 
> {{TypedGuard}} (the standalone TypedGuard API has no pool-size knob; the old 
> timer-thread wiring was dropped in the CAMEL-21857 TypedGuard migration). 
> Either wire it or deprecate it with an upgrade-guide note.
> h3. M5. resilience4j: circuit breaker state and metrics are destroyed on 
> route stop/suspend
> {{ResilienceProcessor.doStop()}} removes the breaker/timelimiter/bulkhead 
> from the registries, so a route suspend/resume or stop/start resets breaker 
> state (an OPEN breaker becomes CLOSED) and zeroes metrics. FT does the same 
> via {{CircuitBreakerMaintenance.reset(id)}}. If intentional, it should at 
> least be documented.
> h3. M6. Documentation errors
> * {{resilience4j-eip.adoc}} "supports two options" table lists 
> {{resilienceConfiguration}} / {{resilienceConfigurationRef}} - neither 
> exists; the actual names are {{resilience4jConfiguration}} and 
> {{configuration}};
> * {{circuitBreaker-eip.adoc}} says "By default, the timeout request is just 
> 1000ms" - timeout is *disabled* by default; 1000ms is only the default 
> duration once enabled;
> * {{resilience4j-eip.adoc}} "If there was no fallback, then the circuit 
> breaker will throw an exception" is wrong for the open-circuit case: by 
> default ({{throwExceptionWhenHalfOpenOrOpenState=false}}) the message 
> continues routing unchanged without an exception (behavior confirmed by 
> {{*RouteRejectedTest}} in both implementations).
> h3. M7. Smaller observations
> * FT {{getCircuitBreakerState()}} uses exception-as-control-flow per message; 
> with a user-supplied {{TypedGuard}} the {{CircuitBreakerMaintenance}} lookup 
> throws on every exchange;
> * when an existing breaker/guard is referenced ({{circuitBreaker}} / 
> {{typedGuard}} option), the remaining inline configuration is silently 
> ignored (FT: timeout/bulkhead; resilience4j: CB config except record/ignore 
> predicates) - should be documented or logged;
> * ignored/not-recorded exceptions are cleared and the message continues with 
> the request body; tested and intended, but it diverges from resilience4j's 
> own semantics (where ignored exceptions still propagate) and deserves an 
> explicit doc note.
> _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