Search code examples
springproject-reactorspring-webfluxcircuit-breakerresilience4j

Resilience4j circuit breaker used with reactive Flux never changes to OPEN on errors


I am evaluating resilience4j to include it in our reactive APIs, so far I am using mock Fluxes.

The service below always fails as I want to test if the circuit OPENs on multiple errors:

@Service
class GamesRepositoryImpl : GamesRepository {

    override fun findAll(): Flux<Game> {
        return if (Math.random() <= 1.0) {
            Flux.error(RuntimeException("fail"))
        } else {
            Flux.just(
                    Game("The Secret of Monkey Island"),
                    Game("Loom"),
                    Game("Maniac Mansion"),
                    Game("Day of the Tentacle")).log()
        }
    }
}

This is the handler that uses the repository, printing the state of the circuit:

@Component
class ApiHandlers(private val gamesRepository: GamesRepository) {

    var circuitBreaker : CircuitBreaker = CircuitBreaker.ofDefaults("gamesCircuitBreaker")

    fun getGames(serverRequest: ServerRequest) : Mono<ServerResponse> {
        println("*********${circuitBreaker.state}")
        return ok().body(gamesRepository.findAll().transform(CircuitBreakerOperator.of(circuitBreaker)), Game::class.java)
    }
}

I invoke the API endpoint many times, always getting this stacktrace:

*********CLOSED
2018-03-14 12:02:28.153 ERROR 1658 --- [ctor-http-nio-3] .a.w.r.e.DefaultErrorWebExceptionHandler : Failed to handle request [GET http://localhost:8081/api/v1/games]

java.lang.RuntimeException: FAIL
    at com.codependent.reactivegames.repository.GamesRepositoryImpl.findAll(GamesRepositoryImpl.kt:12) ~[classes/:na]
    at com.codependent.reactivegames.web.handler.ApiHandlers.getGames(ApiHandlers.kt:20) ~[classes/:na]
    ...

2018-03-14 12:05:48.973 DEBUG 1671 --- [ctor-http-nio-2] i.g.r.c.i.CircuitBreakerStateMachine     : No Consumers: Event ERROR not published
2018-03-14 12:05:48.975 ERROR 1671 --- [ctor-http-nio-2] .a.w.r.e.DefaultErrorWebExceptionHandler : Failed to handle request [GET http://localhost:8081/api/v1/games]

java.lang.RuntimeException: fail
    at com.codependent.reactivegames.repository.GamesRepositoryImpl.findAll(GamesRepositoryImpl.kt:12) ~[classes/:na]
    at com.codependent.reactivegames.web.handler.ApiHandlers.getGames(ApiHandlers.kt:20) ~[classes/:na]
    at com.codependent.reactivegames.web.route.ApiRoutes$apiRouter$1$1$1.invoke(ApiRoutes.kt:14) ~[classes/:na]

As you see the circuit is always CLOSED. I don't know if it has anything to do but notice this message No Consumers: Event ERROR not published.

Why isn't this working?



Solution

  • The problem was the default ringBufferSizeInClosedState which is 100 requests and I never made so many manual requests.

    I setup my own CircuitBreakerConfig for my tests and now the circuit opens right away:

    val circuitBreakerConfig : CircuitBreakerConfig = CircuitBreakerConfig.custom()
            .failureRateThreshold(50f)
            .waitDurationInOpenState(Duration.ofMillis(10000))
            .ringBufferSizeInHalfOpenState(5)
            .ringBufferSizeInClosedState(5)
            .build()
    var circuitBreaker: CircuitBreaker = CircuitBreaker.of("gamesCircuitBreaker", circuitBreakerConfig)