0

I am asking a question similar to this one, and I have tried many different ways of acquiring an existing circuit breaker, created via annotation, within my integration test.

I tried Autowiring in both the main class and test class:

@Autowired
CircuitBreakerRegistry cbr;

But for whatever reason, that doesn't do the trick. I get: Field circuitBreakerRegistry in example.my.class.ClassWithCircuitBreaker required a bean of type 'io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry' that could not be found.

So I tried this:

@Autowired
private ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory;

That succeeds in wiring that object, but when I call reactiveResilience4JCircuitBreakerFactory.getCircuitBreakerRegistry().circuitBreaker("myCB"), it appears to be creating a new instance rather than retrieving an existing one. When I run the test in debug mode, I can see it hit here:

// io.github.resilience4j.circuitbreaker.CircuitBreaker
static CircuitBreaker of(String name, CircuitBreakerConfig circuitBreakerConfig, Map<String, String> tags) {
        return new CircuitBreakerStateMachine(name, circuitBreakerConfig, tags);
    }

That earlier question-and-answer doesn't really answer the question of how to find an existing CB when they are externally configured and created via the @CircuitBreaker annotation. The reason for doing this is twofold - 1) get the existing CB and disable it if a custom configuration is set to true, and, 2) validate that behavior inside an integration test.

The class being tested looks in part like this:

@Component
@Slf4j
@RequiredArgsConstructor
public class ClassWithCircuitBreaker implements SomeInterface {
    
    private static final String CIRCUIT_BREAKER_NAME = "myCB";
    // other stuff, then...
    @Autowired
    CircuitBreakerRegistry circuitBreakerRegistry;

    // annotated method
    @Override
    @CircuitBreaker(name = CIRCUIT_BREAKER_NAME, fallbackMethod = "fallback")
    public ThisStuff doStuff(String term, String id) {
        // do stuff
    }
    
    private ThisStuff fallback(String term, String id, Exception e) {
        log.error("exception while calling doStuff term: {}, id: {}, other thing: {}, exception: {}",
            queryTerm,
            requestId,
            classWithCbConfig.getOtherThing(),
            e
        );
        return ThisStuff.DEFAULT_STUFF;
    }

    @PostConstruct
    private void disableCircuitBreakerMaybe() {
        io.github.resilience4j.circuitbreaker.CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker(CIRCUIT_BREAKER_NAME);
        if(classWithCbConfig.isDisableCircuitBreaker()) {
            log.info("disabling circuit breaker {}", cb.getName());
            cb.transitionToDisabledState();
        } else {
            log.info("circuit breaker {} is in current state {}", CIRCUIT_BREAKER_NAME, cb.getState().name());
        }
    }
}

In the test, I am forcing the call to doStuff to throw an exception. This should result in failed calls being recorded when the CB is not disabled, but I see this output:

log.info("failed metrics :: {}", circuitBreakerRegistry.circuitBreaker("myCB").getMetrics().getNumberOfFailedCalls());
        log.info("success metrics :: {}", circuitBreakerRegistry.circuitBreaker("myCB").getMetrics().getNumberOfSuccessfulCalls());

2023-04-14T09:03:24,695 [main] INFO  example.my.cb.CircuitBreakerTest - failed metrics :: 0
2023-04-14T09:03:24,696 [main] INFO  example.my.cb.CircuitBreakerTest - success metrics :: 0

Project dependencies:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>2.6.6</version>
</dependency>

I have been frustrated by this for the last 24 hours, any help would be much appreciated.

Edit #1

The test class is annotated thusly, before someone suggests the SpringBootTest annotation:

@Slf4j
@SpringBootTest(
        classes = {
                TestConfig.class,
                // various other test configs here...
                CircuitBreakerRegistry.class,
                ReactiveResilience4JCircuitBreakerFactory.class
        }
)
@ActiveProfiles("test")
@ExtendWith(SpringExtension.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)

Update #1

Adding this to the import annotation succeeded in adding the CircuitBreakerRegistry to Spring:

@Import(
        {
                // ..all the other configs
                CircuitBreakerAutoConfiguration.class
        }
)

Technically, I think it added all of the classes from the io.github.resilience4j.circuitbreaker.autoconfigure package. At any rate, problem #1 is solved! The only issue I have left (I think), is that the fallback method is not being invoked even when the base method throws an exception. The metrics are still showing as 0 failed calls.

SCSE
  • 23
  • 8

0 Answers0