Merge pull request #7382 from priyank-sriv/BAEL-3087
BAEL 3087: Exponential Backoff with Jitter
This commit is contained in:
commit
5a13c67fda
|
@ -0,0 +1,2 @@
|
|||
### Relevant Articles:
|
||||
- [Better Retries with Exponential Backoff and Jitter](https://baeldung.com/retries-with-exponential-backoff-and-jitter)
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>backoff-jitter</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito-core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-retry</artifactId>
|
||||
<version>${resilience4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<junit.version>4.12</junit.version>
|
||||
<mockito-core.version>2.27.0</mockito-core.version>
|
||||
<slf4j.version>1.7.26</slf4j.version>
|
||||
<resilience4j.version>0.16.0</resilience4j.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,103 @@
|
|||
package com.baeldung.backoff.jitter;
|
||||
|
||||
import io.github.resilience4j.retry.IntervalFunction;
|
||||
import io.github.resilience4j.retry.Retry;
|
||||
import io.github.resilience4j.retry.RetryConfig;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.baeldung.backoff.jitter.BackoffWithJitterTest.RetryProperties.*;
|
||||
import static io.github.resilience4j.retry.IntervalFunction.ofExponentialBackoff;
|
||||
import static io.github.resilience4j.retry.IntervalFunction.ofExponentialRandomBackoff;
|
||||
import static java.util.Collections.nCopies;
|
||||
import static java.util.concurrent.Executors.newFixedThreadPool;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class BackoffWithJitterTest {
|
||||
|
||||
static Logger log = LoggerFactory.getLogger(BackoffWithJitterTest.class);
|
||||
|
||||
interface PingPongService {
|
||||
|
||||
String call(String ping) throws PingPongServiceException;
|
||||
}
|
||||
|
||||
class PingPongServiceException extends RuntimeException {
|
||||
|
||||
public PingPongServiceException(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
|
||||
private PingPongService service;
|
||||
private static final int NUM_CONCURRENT_CLIENTS = 8;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
service = mock(PingPongService.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRetryExponentialBackoff_thenRetriedConfiguredNoOfTimes() {
|
||||
IntervalFunction intervalFn = ofExponentialBackoff(INITIAL_INTERVAL, MULTIPLIER);
|
||||
Function<String, String> pingPongFn = getRetryablePingPongFn(intervalFn);
|
||||
|
||||
when(service.call(anyString())).thenThrow(PingPongServiceException.class);
|
||||
try {
|
||||
pingPongFn.apply("Hello");
|
||||
} catch (PingPongServiceException e) {
|
||||
verify(service, times(MAX_RETRIES)).call(anyString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRetryExponentialBackoffWithoutJitter_thenThunderingHerdProblemOccurs() throws InterruptedException {
|
||||
IntervalFunction intervalFn = ofExponentialBackoff(INITIAL_INTERVAL, MULTIPLIER);
|
||||
test(intervalFn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRetryExponentialBackoffWithJitter_thenRetriesAreSpread() throws InterruptedException {
|
||||
IntervalFunction intervalFn = ofExponentialRandomBackoff(INITIAL_INTERVAL, MULTIPLIER, RANDOMIZATION_FACTOR);
|
||||
test(intervalFn);
|
||||
}
|
||||
|
||||
private void test(IntervalFunction intervalFn) throws InterruptedException {
|
||||
Function<String, String> pingPongFn = getRetryablePingPongFn(intervalFn);
|
||||
ExecutorService executors = newFixedThreadPool(NUM_CONCURRENT_CLIENTS);
|
||||
List<Callable<String>> tasks = nCopies(NUM_CONCURRENT_CLIENTS, () -> pingPongFn.apply("Hello"));
|
||||
|
||||
when(service.call(anyString())).thenThrow(PingPongServiceException.class);
|
||||
|
||||
executors.invokeAll(tasks);
|
||||
}
|
||||
|
||||
private Function<String, String> getRetryablePingPongFn(IntervalFunction intervalFn) {
|
||||
RetryConfig retryConfig = RetryConfig.custom()
|
||||
.maxAttempts(MAX_RETRIES)
|
||||
.intervalFunction(intervalFn)
|
||||
.retryExceptions(PingPongServiceException.class)
|
||||
.build();
|
||||
Retry retry = Retry.of("pingpong", retryConfig);
|
||||
return Retry.decorateFunction(retry, ping -> {
|
||||
log.info("Invoked at {}", LocalDateTime.now());
|
||||
return service.call(ping);
|
||||
});
|
||||
}
|
||||
|
||||
static class RetryProperties {
|
||||
static final Long INITIAL_INTERVAL = 1000L;
|
||||
static final Double MULTIPLIER = 2.0D;
|
||||
static final Double RANDOMIZATION_FACTOR = 0.6D;
|
||||
static final Integer MAX_RETRIES = 4;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
<module>design-patterns-2</module>
|
||||
<module>solid</module>
|
||||
<module>dip</module>
|
||||
<module>backoff-jitter</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
Loading…
Reference in New Issue