I have an existing application on spring boot 2.4.4 running on Java 11. I am migrating to Java 17 and Spring boot 2.7.10. I am using feign-reactor-core to enable feign and have configured a retry policy
pom.xml
<dependency>
<groupId>com.playtika.reactivefeign</groupId>
<artifactId>feign-reactor-cloud</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.playtika.reactivefeign</groupId>
<artifactId>feign-reactor-spring-configuration</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.playtika.reactivefeign</groupId>
<artifactId>feign-reactor-webclient</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.projectreactor.spring</groupId>
<artifactId>reactor-spring-context</artifactId>
<version>1.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.28</version>
</dependency>
Configuration
@Configuration
public class ReactiveFeignClientConfiguration {
@Bean
public ReactiveRetryPolicy reactiveRetryPolicy(final ReactiveFeignRetryConfiguration reactiveFeignRetryConfiguration) {
return new FilteredReactiveRetryPolicy(new MyFeignReactiveRetryPolicy(Schedulers.parallel(), reactiveFeignRetryConfiguration),
throwable -> throwable instanceof RetryableException || throwable instanceof ReadTimeoutException);
}
}
MyFeignReactiveRetryPolicy.java
package com.my.client.reactive.rest.client.retry;
import feign.ExceptionPropagationPolicy;
import lombok.extern.slf4j.Slf4j;
import com.my.client.reactive.rest.client.config.ReactiveFeignRetryConfiguration;
import reactivefeign.retry.SimpleReactiveRetryPolicy;
import reactor.core.scheduler.Scheduler;
@Slf4j
public class MyFeignReactiveRetryPolicy extends SimpleReactiveRetryPolicy {
private final int maxAttempts;
private final long interval;
private final long maxInterval;
public MyFeignReactiveRetryPolicy(Scheduler scheduler, ReactiveFeignRetryConfiguration reactiveFeignRetryConfiguration) {
super(scheduler, ExceptionPropagationPolicy.UNWRAP);
this.maxAttempts = reactiveFeignRetryConfiguration.getMaxAttempts();
this.interval = reactiveFeignRetryConfiguration.getInterval();
this.maxInterval = reactiveFeignRetryConfiguration.getMaxInterval();
}
@Override
public long retryDelay(Throwable error, int attemptNo) {
log.info("Feign attempt {} due to {}", attemptNo, error.getMessage());
if (attemptNo == maxAttempts) {
log.error("Max Attempts reached - {}", attemptNo);
log.error("Error after max attempts - ", error);
return -1;
}
return this.interval;
}
}
Test Controller
@Slf4j
@RestController
public class ReactiveTestController {
@Autowired
private IIntegrationTestController controller;
@PostMapping("/submitRequest")
public Mono<String> submitTestRequest(@RequestBody final Object obj) {
log.info("Inside controller with content :{}", obj);
return controller.checkHealth();
}
}
@ReactiveFeignClient(name = "test-client")
public interface IIntegrationTestController {
@GetMapping("/feigntest/health")
Mono<String> checkHealth();
}
Test class
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = {MyApplication.class}) // spring runner application, no additional code
@AutoConfigureWebTestClient(timeout = "600000")
@DirtiesContext
public class Test {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private WebTestClient webTestClient;
@Autowired
private WireMockServer mockHttpServer;
@Test
void testFeign() throws Exception {
mockHttpServer.start();
final StubMapping stubMapping = stubFor(get(urlEqualTo("/feigntest/health"))
.willReturn(aResponse().withHeader("Content-Type", "application/json")
.withBody(objectMapper.writeValueAsString(new APIError(new FeignClientException(500, "error-error"), 500, "error")))
.withStatus(500)));
Object obj = new Object();
WebTestClient.ResponseSpec exchange = webTestClient.post().uri("/submitRequest")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(obj)
.exchange();
exchange.expectStatus().is5xxServerError();
exchange.expectBody().consumeWith(body -> {
Assertions.assertNotNull(body.getResponseBody());
});
mockHttpServer.stop();
}
}
I am getting success from this test case in Java 17 with Spring 2.7.10, but its taking a lot of time in my system (MacOS) ~26 seconds
In Java 11 with Spring boot 2.4.4, this didn't take this long (gets success in under a second).
Also If I remove the ReactiveRetryPolicy bean, then its working fine and getting successful in less than a second in the upgraded versions.
What is going wrong here?