allthingssecurity opened a new pull request, #27038:
URL: https://github.com/apache/camel/pull/27038

   # Description
   
   [CAMEL-25129](https://issues.apache.org/jira/browse/CAMEL-25129)
   
   With `timeoutEnabled` and `bulkheadEnabled`, the Circuit Breaker EIP with 
resilience4j releases the bulkhead permit when the call times out, while the 
call keeps running on the timeout thread pool. Every next message gets a permit 
and starts one more call, so `bulkheadMaxConcurrentCalls` does not limit the 
calls running against a slow service, which is when the bulkhead matters. The 
concurrency is then bounded only by the timeout thread pool.
   
   `ResilienceProcessor` applies the bulkhead around the time limiter: in sync 
mode `Bulkhead.decorateCallable` over 
`TimeLimiter.decorateFutureSupplier(supplyAsync(task))`, in async mode 
`Bulkhead.decorateCompletionStage` over `TimeLimiter.decorateCompletionStage`. 
The permit is released when the time limiter gives up, and 
`CompletableFuture.cancel` does not interrupt the task. The order comes from 
CAMEL-17095, a one-line change so that the bulkhead decorates the time limited 
callable instead of the bare task; the async mode (CAMEL-24209) copied it.
   
   Resilience4j documents the order 
`Retry(CircuitBreaker(RateLimiter(TimeLimiter(Bulkhead(function)))))`, and its 
README example for asynchronous calls applies the bulkhead first, then the time 
limiter, then the circuit breaker. camel-microprofile-fault-tolerance already 
holds the permit for the whole call: SmallRye applies the bulkhead inside the 
timeout, and its synchronous timeout returns only when the invocation has 
returned. So it is not changed here.
   
   This change:
   - sync mode: the `supplyAsync` stage is decorated with 
`Bulkhead.decorateCompletionStage`, and `TimeLimiter.decorateFutureSupplier` is 
applied over it. Without timeout the `Bulkhead.decorateCallable` is kept as 
before.
   - async mode: `Bulkhead.decorateCompletionStage` is applied before 
`TimeLimiter.decorateCompletionStage`.
   - The permit is released when the call ends. The caller still waits for a 
permit (`bulkheadMaxWaitDuration`) before the timeout starts, and a full 
bulkhead still reaches the fallback as `BulkheadFullException`, so 
`CamelCircuitBreakerResponseRejected`, the bulkhead-rejected counter and the 
circuit breaker's error recording are the same.
   - Upgrade guide note for 4.23: while calls that timed out are still running, 
further calls are rejected by the bulkhead where they used to be started, and a 
call that never ends keeps its permit.
   
   The fallback's write guard (`exchangeWriteGuard.set(true)` against the 
worker's compare-and-set, a residual of CAMEL-24134) is a separate issue and is 
not changed here.
   
   Tests:
   - New `ResilienceBulkheadTimeoutTest`, sync and `asynchronous(true)`: 
bulkhead of 1, timeout 200 ms, a protected route that blocks on a latch and 
counts the calls running at once. Three messages are sent one after another: 
the first times out and gets the fallback, the next two must be rejected by the 
bulkhead (`CamelCircuitBreakerResponseRejected` true, bulkhead-rejected counter 
2) and not start. After the latch is released, a new call succeeds, so the 
permit is released when the slow call ends.
   - Without the change both tests fail (`call 2 should be rejected by the 
bulkhead ==> expected: <true> but was: <false>`: the second call started while 
the first was still running).
   - With the change, all camel-resilience4j tests: 77 tests, 0 failures, 0 
errors.
   
   Found with a TLA+ model of callers, the time limiter, the worker and the 
bulkhead, which finds the trace in 6 steps and holds with the bulkhead inside 
the time limiter for 3 calls and 1 or 2 permits. I then reproduced it with the 
real processor in both modes.
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL) filed for 
the change (usually before you start working on it).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
     (I built and tested the affected module, including the formatter and 
import-sort plugins. I did not run the full root build.)
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
     This PR was prepared with Claude Code (Claude Opus 5.5). The commit 
carries a `Co-Authored-By` trailer.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to