Skip to content

Commit

Permalink
Fix CircuitBreaker docs (#357)
Browse files Browse the repository at this point in the history
  • Loading branch information
serras authored Jan 25, 2025
1 parent b72a353 commit fec8a50
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 17 deletions.
28 changes: 18 additions & 10 deletions content/docs/learn/resilience/circuitbreaker.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ graph LR;
- This is the state in which the circuit breaker starts.
- Requests are made normally in this state:
- When an exception occurs, it increments the failure counter.
- When the failure counter reaches the given `maxFailures` threshold,
- When the failure counter **exceeds** the given `maxFailures` threshold,
the breaker moves to the _Open_ state.
- A successful request will reset the failure counter to zero.

Expand Down Expand Up @@ -76,9 +76,9 @@ in _Cloud Design Patterns_.
Arrow offers different strategies to determine when the circuit breaker should open and short-circuit all incoming requests. The currently available ones are:

- [_Count_](https://apidocs.arrow-kt.io/arrow-resilience/arrow.resilience/-circuit-breaker/-opening-strategy/-count/index.html).
This strategy sets a maximum number of failures. Once this threshold is reached, the circuit breaker moves to _Open_.
This strategy sets a maximum number of failures. Once this threshold is exceeded, the circuit breaker moves to _Open_.
Note that every time a request succeeds, the counter is set back to zero; the circuit breaker only
moves to _Open_ when the maximum number of failures happen **consecutively**.
moves to _Open_ when the aforementioned number of failures happen **consecutively**.
- [_Sliding Window_](https://apidocs.arrow-kt.io/arrow-resilience/arrow.resilience/-circuit-breaker/-opening-strategy/-sliding-window/index.html).
This strategy counts the number of failures within a given time window. Unlike the `Count` approach, the circuit breaker
will only move to `Open` if the number of failing requests tracked within the given period exceeds the threshold. As the
Expand All @@ -104,6 +104,14 @@ potentially fail with [`protectOrThrow`](https://apidocs.arrow-kt.io/arrow-resil
want this error to be communicated back. If the error arises, the internal state
of the circuit breaker also changes.

:::note Failure means exception

The `protectEither` function has a slightly misleading name. The `Either`
refers to the fact that it returns `Either<ExecutionRejected, A>`, not to
signalling errors _within_ the protected block by returning `Either.Left`.

:::

<!--- INCLUDE
import arrow.core.Either
import arrow.resilience.CircuitBreaker
Expand All @@ -115,8 +123,9 @@ import kotlinx.coroutines.delay
```kotlin
@ExperimentalTime
suspend fun main(): Unit {
val maxFailuresUntilOpen = 2
val circuitBreaker = CircuitBreaker(
openingStrategy = OpeningStrategy.Count(2),
openingStrategy = OpeningStrategy.Count(maxFailuresUntilOpen),
resetTimeout = 2.seconds,
exponentialBackoffFactor = 1.2,
maxResetTimeout = 60.seconds,
Expand All @@ -126,12 +135,11 @@ suspend fun main(): Unit {
circuitBreaker.protectOrThrow { "I am in Closed: ${circuitBreaker.state()}" }.also(::println)

// simulate service getting overloaded
Either.catch {
circuitBreaker.protectOrThrow { throw RuntimeException("Service overloaded") }
}.also(::println)
Either.catch {
circuitBreaker.protectOrThrow { throw RuntimeException("Service overloaded") }
}.also(::println)
for (i in 1 .. maxFailuresUntilOpen + 1) {
Either.catch {
circuitBreaker.protectOrThrow { throw RuntimeException("Service overloaded") }
}.also(::println)
}
circuitBreaker.protectEither { }
.also { println("I am Open and short-circuit with ${it}. ${circuitBreaker.state()}") }

Expand Down
14 changes: 7 additions & 7 deletions guide/src/test/kotlin/examples/example-circuitbreaker-01.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import kotlinx.coroutines.delay

@ExperimentalTime
suspend fun main(): Unit {
val maxFailuresUntilOpen = 2
val circuitBreaker = CircuitBreaker(
openingStrategy = OpeningStrategy.Count(2),
openingStrategy = OpeningStrategy.Count(maxFailuresUntilOpen),
resetTimeout = 2.seconds,
exponentialBackoffFactor = 1.2,
maxResetTimeout = 60.seconds,
Expand All @@ -21,12 +22,11 @@ suspend fun main(): Unit {
circuitBreaker.protectOrThrow { "I am in Closed: ${circuitBreaker.state()}" }.also(::println)

// simulate service getting overloaded
Either.catch {
circuitBreaker.protectOrThrow { throw RuntimeException("Service overloaded") }
}.also(::println)
Either.catch {
circuitBreaker.protectOrThrow { throw RuntimeException("Service overloaded") }
}.also(::println)
for (i in 1 .. maxFailuresUntilOpen + 1) {
Either.catch {
circuitBreaker.protectOrThrow { throw RuntimeException("Service overloaded") }
}.also(::println)
}
circuitBreaker.protectEither { }
.also { println("I am Open and short-circuit with ${it}. ${circuitBreaker.state()}") }

Expand Down

0 comments on commit fec8a50

Please sign in to comment.