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]
