spring-cloud/spring-cloud-circuitbreaker

Spring Boot 2 + resilience4j - fallback method not being called in integration test

scurtis-disco opened this issue · 17 comments

I originally asked this as an SO question and then as in issue in the resilience4j project, and RobWin pointed me over here. To save clicks, I'll copy-paste the full text of the GH issue below.

=== START COPY-🍝 ===

Resilience4j version:
1.7.0

Spring Cloud Starter version:
2.1.0

Java version:
17

I posted an SO question laying out my problem. Initially, I couldn't get the CB instance from the registry, but I solved that issue by importing CircuitBreakerAutoConfiguration.class. Now, the other issue is that the fallback method isn't being invoked. It works when the code is deployed but not in the integration test. I verified by debugging in my IDE that the CB is being created, with the configs I have defined in a test YAML.

resilience4j:
  circuitbreaker:
    instances:
      pmiServiceCircuitBreaker:
        minimumNumberOfCalls: 5
        slidingWindowSize: 10
        slidingWindowType: COUNT_BASED
        failureRateThreshold: 50 # percentage
        slowCallRateThreshold: 100 # percentage for slowCallDurationThreshold
        slowCallDurationThreshold: 200ms # milliseconds, should this be lower?
        permittedNumberOfCallsInHalfOpenState: 5
        waitDurationInOpenState: 1s # seconds

The simple test looks like:

@slf4j
https://github.com/import(
{

            TestConfig.class,
            CircuitBreakerAutoConfiguration.class
    }
)
https://github.com/SpringBootTest(
classes = {
GrpcTestServer.class,
TestGrpcClientConfigSpy.class,
ClassWithCircuitBreaker.class
}
)
@activeprofiles("test")
@ExtendWith(SpringExtension.class)
@testinstance(TestInstance.Lifecycle.PER_CLASS)
public class ClassWithCircuitBreakerTest {

@Autowired
private TestGrpcClientConfigSpy grpcClientConfigSpy;

@Autowired
CircuitBreakerRegistry circuitBreakerRegistry;

@SpyBean
@Autowired
ClassWithCircuitBreaker cwcb;


@DisplayName("ClassWithCircuitBreaker - circuit breaker enabled")
@Test
void testCircuitBreaker_enabled() throws IOException {
    circuitBreakerRegistry.getAllCircuitBreakers().forEach(cb -> {
        log.info("found circuit breaker :: {}", cb.getName());
        log.info("circuit breaker state is :: {}", cb.getState().name());
    });

    int failedCalls = 0;

    Stream.rangeClosed(1,10).forEach(count -> {
        try {
            cwcb.doStuff("blah", "test-id-123");
        } catch(StatusRuntimeException sre) {
            log.warn("exceptiones? {}", sre.getMessage());
        };
    });

    CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("myCB");
    CircuitBreaker.Metrics metrics = cb.getMetrics();

    log.info("configs :: {}", cb.getCircuitBreakerConfig());
    log.info("failed metrics :: {}", metrics.getNumberOfFailedCalls());
    log.info("success metrics :: {}", metrics.getNumberOfSuccessfulCalls());
    log.info("state after test :: {}", cb.getState().name());
    assertTrue(true);
}
}

The class I am testing is a gRPC client, so the GrpcTestServer class sets up a server and for this test, it's configured to throw a StatusRuntimeException and I see that being logged in the test, but the fallback method is never invoked, and all of the metrics are zero and the circuit stays closed. Again, this works when deployed, it's only the integration test that is not working. Any help is much appreciated.

=== END COPY-🍝 ===

Can you provide a complete, minimal, verifiable sample that reproduces the problem? It should be available as a GitHub (or similar) project or attached to this issue as a zip file.

@ryanjbaxter I will try to get to that sometime this week.

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

@spring-cloud-issues @ryanjbaxter
I have pushed a minimal example to the repo now, that includes a simple protobuf to use as the external service via running
mvn compile before running the unit tests.

I have 2 unit tests that log the circuit breaker and its state, and it appears to work correctly (still not sure why it's not working in my real project). HOWEVER, I found a different issue that I don't understand, namely that my custom runtime exception doesn't trigger the fallback method. I confirmed this via logging, and debugging in my Intellij IDE and setting a breakpoint inside the method, and the code never hits that breakpoint.

Any help to understand this is much appreciated.

Is this going to be re-opened now that there is an example?

we had the same issue and adding this dependency fixed it :

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

@guerricm

I already have that dependency in the example.

EDIT: that does fix the issue of instantiating the circuit breaker, so we've got that going for us, which is nice. 🤣

This seems like a good time to point out again, even though it's explicit with the title above, this only affects testing, the real out-in-the-wild code works as expected. I just want to understand the difference between the environment when testing vs. the deployed production code.

I don't see a link to the repo in your comment.

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@ryanjbaxter @spring-cloud-issues my apologies, I thought I had already linked to it earlier in this post.

Here is my example

That link gives me a 404

This is not a minimally project, please make it as simple as possible. In addition you are using versions of Spring Boot and Spring Cloud that are no longer supported. Please recreate a sample that is a barebones as possible that demonstrates the problem using supported versions of Spring Boot and Spring Cloud and provide details of how to use the same to reproduce the problem in the README.

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.