This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch circuit-breaker-rejected
in repository https://gitbox.apache.org/repos/asf/camel-jbang-examples.git

commit f631aaf013ea13a4331c1cb770a204da7def4ad5
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Oct 20 09:55:47 2026 +0200

    fail-well/circuit-breaker - the fallback tells a failed call from one the 
open breaker did not make
    
    On Camel 4.23 CamelCircuitBreakerResponseRejected is set in onFallback 
(CAMEL-24818), so the fallback answers
    "supplier failed" when the call was made and failed, and "breaker open, 
supplier not called" when the open breaker
    rejected it. The README explains both and points at camel get 
circuit-breaker and its new fallback, rejected and
    timeout counters (CAMEL-24819); the test checks both lines. Closes #81.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 README.md                                          |  2 +-
 camel-jbang-example-catalog.json                   |  2 +-
 fail-well/README.md                                |  2 +-
 fail-well/circuit-breaker/README.md                | 37 +++++++++++++++-------
 .../circuit-breaker/circuit-breaker.camel.yaml     | 21 +++++++++---
 fail-well/circuit-breaker/metadata.json            |  2 +-
 .../test/circuit-breaker.citrus.it.yaml            |  7 +++-
 7 files changed, 52 insertions(+), 21 deletions(-)

diff --git a/README.md b/README.md
index 278df37..4d4304d 100644
--- a/README.md
+++ b/README.md
@@ -84,7 +84,7 @@ Retries, a dead letter channel, and a circuit breaker in 
front of a flaky servic
 | Example | What you will see | Needs |
 |---|---|---|
 | [Error handling](fail-well/error-handling/) | The three orders go to a 
payment provider: the first is charged at once, the second gets no answer twice 
and is charged on the third attempt after two retries logged as warnings, and 
the third is declined, logged as such and parked as a file for manual review. | 
nothing |
-| [Circuit breaker](fail-well/circuit-breaker/) | A stock check calls the 
supplier every second; the supplier goes down for nine calls, the breaker opens 
after two failures in its window of four, answers from the fallback while open, 
tries the supplier again after five seconds and closes once a call succeeds; 
each line logs the breaker state. | nothing |
+| [Circuit breaker](fail-well/circuit-breaker/) | A stock check calls the 
supplier every second; the supplier goes down for nine calls, the breaker opens 
after two failures in its window of four, answers from the fallback without 
calling the supplier while open, tries it again after five seconds and closes 
once a call succeeds; each line logs the breaker state and whether the supplier 
failed or was not called. | nothing |
 
 ### [Connect without a service](connect/)
 
diff --git a/camel-jbang-example-catalog.json b/camel-jbang-example-catalog.json
index 54d2fc4..3dfd6ef 100644
--- a/camel-jbang-example-catalog.json
+++ b/camel-jbang-example-catalog.json
@@ -868,7 +868,7 @@
     {
         "name": "fail-well/circuit-breaker",
         "title": "Circuit breaker",
-        "description": "A stock check calls the supplier every second; the 
supplier goes down for nine calls, the breaker opens after two failures in its 
window of four, answers from the fallback while open, tries the supplier again 
after five seconds and closes once a call succeeds; each line logs the breaker 
state.",
+        "description": "A stock check calls the supplier every second; the 
supplier goes down for nine calls, the breaker opens after two failures in its 
window of four, answers from the fallback without calling the supplier while 
open, tries it again after five seconds and closes once a call succeeds; each 
line logs the breaker state and whether the supplier failed or was not called.",
         "level": "fail-well",
         "teaches": {
             "components": [
diff --git a/fail-well/README.md b/fail-well/README.md
index 3612799..8e50e0f 100644
--- a/fail-well/README.md
+++ b/fail-well/README.md
@@ -6,7 +6,7 @@ Retries, a dead letter channel, and a circuit breaker in front 
of a flaky servic
 | Example | What you will see | Needs |
 |---|---|---|
 | [Error handling](error-handling/) | The three orders go to a payment 
provider: the first is charged at once, the second gets no answer twice and is 
charged on the third attempt after two retries logged as warnings, and the 
third is declined, logged as such and parked as a file for manual review. | 
nothing |
-| [Circuit breaker](circuit-breaker/) | A stock check calls the supplier every 
second; the supplier goes down for nine calls, the breaker opens after two 
failures in its window of four, answers from the fallback while open, tries the 
supplier again after five seconds and closes once a call succeeds; each line 
logs the breaker state. | nothing |
+| [Circuit breaker](circuit-breaker/) | A stock check calls the supplier every 
second; the supplier goes down for nine calls, the breaker opens after two 
failures in its window of four, answers from the fallback without calling the 
supplier while open, tries it again after five seconds and closes once a call 
succeeds; each line logs the breaker state and whether the supplier failed or 
was not called. | nothing |
 
 Start with [Error handling](error-handling/); the examples read best in the 
order above, each one building on what the one before it set up.
 
diff --git a/fail-well/circuit-breaker/README.md 
b/fail-well/circuit-breaker/README.md
index c38d159..a688f20 100644
--- a/fail-well/circuit-breaker/README.md
+++ b/fail-well/circuit-breaker/README.md
@@ -7,14 +7,18 @@ fallback without calling the supplier, and tries again after 
five seconds until
 ## What you will see
 
 ```text
-INFO ... circuit-breaker.camel.yaml:30 : Stock check 1 (breaker CLOSED): 
CAMEL-MUG: 47 in stock at the supplier
-INFO ... circuit-breaker.camel.yaml:30 : Stock check 2 (breaker CLOSED): 
CAMEL-MUG: 35 in stock at the supplier
-INFO ... circuit-breaker.camel.yaml:30 : Stock check 3 (breaker CLOSED): 
CAMEL-MUG: 49 in stock at the supplier
-INFO ... circuit-breaker.camel.yaml:30 : Stock check 4 (breaker CLOSED): no 
answer, using last known stock
-INFO ... circuit-breaker.camel.yaml:30 : Stock check 5 (breaker OPEN): no 
answer, using last known stock
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 1 (breaker CLOSED): 
CAMEL-MUG: 24 in stock at the supplier
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 2 (breaker CLOSED): 
CAMEL-MUG: 44 in stock at the supplier
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 3 (breaker CLOSED): 
CAMEL-MUG: 31 in stock at the supplier
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 4 (breaker CLOSED): 
supplier failed, using last known stock
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 5 (breaker OPEN): 
supplier failed, using last known stock
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 6 (breaker OPEN): breaker 
open, supplier not called, using last known stock
 ...
-INFO ... circuit-breaker.camel.yaml:30 : Stock check 14 (breaker OPEN): no 
answer, using last known stock
-INFO ... circuit-breaker.camel.yaml:30 : Stock check 15 (breaker CLOSED): 
CAMEL-MUG: 58 in stock at the supplier
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 10 (breaker OPEN): 
supplier failed, using last known stock
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 11 (breaker OPEN): 
breaker open, supplier not called, using last known stock
+...
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 14 (breaker OPEN): 
breaker open, supplier not called, using last known stock
+INFO ... circuit-breaker.camel.yaml:43 : Stock check 15 (breaker CLOSED): 
CAMEL-MUG: 46 in stock at the supplier
 ```
 
 ## Install Camel CLI
@@ -35,13 +39,18 @@ Stop it with `ctrl` + `c`; the state logged on each line is 
the breaker's state
 - `circuitBreaker` wraps the call to `direct:supplier` with 
[Resilience4j](https://resilience4j.readme.io/):
   the breaker counts the last four calls (`slidingWindowSize`) and opens when 
half of them failed
   (`failureRateThreshold`), which happens on check 5.
-- While open, the steps are not run at all; `onFallback` sets the body 
instead. After `waitDurationInOpenState`,
-  five seconds, the breaker goes half open and lets one call through 
(`permittedNumberOfCallsInHalfOpenState`):
-  check 10 still fails, so it opens for another five seconds; check 15 
succeeds and closes it.
+- `onFallback` runs both when a call fails and when the open breaker does not 
make the call at all, and
+  `CamelCircuitBreakerResponseRejected` tells them apart: `false` on checks 4 
and 5, where the supplier was
+  called and failed (the failure on check 5 opens the breaker, so the state 
after it is `OPEN`), and `true` on
+  check 6 onwards, where the steps are not run at all. That difference is what 
the breaker buys: no call to a
+  service that is known to be down.
+- After `waitDurationInOpenState`, five seconds, the breaker goes half open 
and lets one call through
+  (`permittedNumberOfCallsInHalfOpenState`): check 10 is that call and still 
fails, so it opens for another five
+  seconds; check 15 succeeds and closes it.
 - The supplier is a second route with `noErrorHandler`, so its exception 
reaches the breaker; it is down while
   the timer counter is 4 to 12.
-- `CamelCircuitBreakerState` is one of the exchange properties the breaker 
sets; `CamelCircuitBreakerResponseSuccessfulExecution`
-  and `CamelCircuitBreakerResponseFromFallback` are the others.
+- `CamelCircuitBreakerState` and `CamelCircuitBreakerResponseRejected` are 
exchange properties the breaker sets;
+  `CamelCircuitBreakerResponseSuccessfulExecution` and 
`CamelCircuitBreakerResponseFromFallback` are others.
 
 ## Build it step by step
 
@@ -51,9 +60,13 @@ Stop it with `ctrl` + `c`; the state logged on each line is 
the breaker's state
 3. Wrap the call in `circuitBreaker` with `onFallback`: no more stack traces, 
but the supplier is still called
    nine times because the default window is 100 calls.
 4. Add the `resilience4jConfiguration` shown here and log 
`CamelCircuitBreakerState` to watch it open and close.
+5. Choose the fallback body on `CamelCircuitBreakerResponseRejected`, so the 
log shows which checks the breaker
+   saved from calling the supplier.
 
 ## Try changing
 
+- Run `camel get circuit-breaker` in another terminal while it runs: the state 
and the success, failure,
+  rejected, fallback and timeout counters of the breaker.
 - Set `waitDurationInOpenState` to 2000 and see the breaker try the supplier 
sooner.
 - Add `timeoutEnabled: true` and `timeoutDuration: 500` and a `delay` of 1000 
in the supplier: a slow supplier
   counts as a failure too.
diff --git a/fail-well/circuit-breaker/circuit-breaker.camel.yaml 
b/fail-well/circuit-breaker/circuit-breaker.camel.yaml
index 90d73bf..87f6ef9 100644
--- a/fail-well/circuit-breaker/circuit-breaker.camel.yaml
+++ b/fail-well/circuit-breaker/circuit-breaker.camel.yaml
@@ -23,10 +23,23 @@
                   uri: direct:supplier
             onFallback:
               steps:
-                - setBody:
-                    expression:
-                      simple:
-                        expression: "no answer, using last known stock"
+                # rejected: the breaker is open, so the supplier was not 
called at all
+                - choice:
+                    when:
+                      - expression:
+                          simple:
+                            expression: 
"${exchangeProperty.CamelCircuitBreakerResponseRejected} == true"
+                        steps:
+                          - setBody:
+                              expression:
+                                simple:
+                                  expression: "breaker open, supplier not 
called, using last known stock"
+                    otherwise:
+                      steps:
+                        - setBody:
+                            expression:
+                              simple:
+                                expression: "supplier failed, using last known 
stock"
         - log:
             message: "Stock check ${header.CamelTimerCounter} (breaker 
${exchangeProperty.CamelCircuitBreakerState}): ${body}"
 
diff --git a/fail-well/circuit-breaker/metadata.json 
b/fail-well/circuit-breaker/metadata.json
index 21bb5fc..07753a1 100644
--- a/fail-well/circuit-breaker/metadata.json
+++ b/fail-well/circuit-breaker/metadata.json
@@ -1,6 +1,6 @@
 {
     "title": "Circuit breaker",
-    "description": "A stock check calls the supplier every second; the 
supplier goes down for nine calls, the breaker opens after two failures in its 
window of four, answers from the fallback while open, tries the supplier again 
after five seconds and closes once a call succeeds; each line logs the breaker 
state.",
+    "description": "A stock check calls the supplier every second; the 
supplier goes down for nine calls, the breaker opens after two failures in its 
window of four, answers from the fallback without calling the supplier while 
open, tries it again after five seconds and closes once a call succeeds; each 
line logs the breaker state and whether the supplier failed or was not called.",
     "level": "fail-well",
     "order": 2,
     "teaches": {
diff --git a/fail-well/circuit-breaker/test/circuit-breaker.citrus.it.yaml 
b/fail-well/circuit-breaker/test/circuit-breaker.citrus.it.yaml
index 8821505..068b417 100644
--- a/fail-well/circuit-breaker/test/circuit-breaker.citrus.it.yaml
+++ b/fail-well/circuit-breaker/test/circuit-breaker.citrus.it.yaml
@@ -13,7 +13,12 @@ actions:
       jbang:
         verify:
           integration: "circuit-breaker"
-          logMessage: "(breaker OPEN): no answer"
+          logMessage: "(breaker OPEN): supplier failed, using last known stock"
+  - camel:
+      jbang:
+        verify:
+          integration: "circuit-breaker"
+          logMessage: "(breaker OPEN): breaker open, supplier not called, 
using last known stock"
   - camel:
       jbang:
         verify:

Reply via email to