Merge branch 'master' into bael-3090

This commit is contained in:
dupirefr
2019-08-16 10:09:53 +02:00
1150 changed files with 224944 additions and 4675 deletions
+2
View File
@@ -0,0 +1,2 @@
### Relevant Articles:
- [Better Retries with Exponential Backoff and Jitter](https://www.baeldung.com/resilience4j-backoff-jitter)
+54
View File
@@ -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;
}
}
+13
View File
@@ -36,6 +36,17 @@
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.inferred</groupId>
<artifactId>freebuilder</artifactId>
<version>${freebuilder.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>${javax.annotations.version}</version>
</dependency>
</dependencies>
@@ -45,5 +56,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<intellij.annotations.version>16.0.2</intellij.annotations.version>
<assertj.version>3.12.2</assertj.version>
<freebuilder.version>2.4.1</freebuilder.version>
<javax.annotations.version>3.0.2</javax.annotations.version>
</properties>
</project>
@@ -0,0 +1,25 @@
package com.baeldung.freebuilder;
import org.inferred.freebuilder.FreeBuilder;
import java.util.Optional;
@FreeBuilder
public interface Address {
Optional<String> getAddressLine1();
Optional<String> getAddressLine2();
Optional<String> getAddressLine3();
String getCity();
Optional<String> getState();
Optional<Long> getPinCode();
class Builder extends Address_Builder {
}
}
@@ -0,0 +1,65 @@
package com.baeldung.freebuilder;
import org.inferred.freebuilder.FreeBuilder;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@FreeBuilder
public interface Employee {
String getName();
int getAge();
String getDepartment();
String getRole();
String getSupervisorName();
String getDesignation();
String getEmail();
long getPhoneNumber();
Optional<Boolean> getPermanent();
Optional<String> getDateOfJoining();
@Nullable
String getCurrentProject();
Address getAddress();
List<Long> getAccessTokens();
Map<String, Long> getAssetsSerialIdMapping();
Optional<Double> getSalaryInUSD();
class Builder extends Employee_Builder {
public Builder() {
// setting default value for department
setDepartment("Builder Pattern");
}
@Override
public Builder setEmail(String email) {
if (checkValidEmail(email))
return super.setEmail(email);
else
throw new IllegalArgumentException("Invalid email");
}
private boolean checkValidEmail(String email) {
return email.contains("@");
}
}
}
@@ -0,0 +1,53 @@
package com.baeldung.freebuilder.builder;
public class Employee {
private final String name;
private final int age;
private final String department;
private Employee(String name, int age, String department) {
this.name = name;
this.age = age;
this.department = department;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getDepartment() {
return department;
}
public static class Builder {
private String name;
private int age;
private String department;
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setDepartment(String department) {
this.department = department;
return this;
}
public Employee build() {
return new Employee(name, age, department);
}
}
}
@@ -0,0 +1,226 @@
package com.baeldung.freebuilder;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class EmployeeBuilderUnitTest {
private static final int PIN_CODE = 223344;
public static final String CITY_NAME = "New York";
public static final int INPUT_SALARY_EUROS = 10000;
public static final double EUROS_TO_USD_RATIO = 0.6;
@Test
public void whenBuildEmployeeWithAddress_thenReturnEmployeeWithValidAddress() {
// when
Address.Builder addressBuilder = new Address.Builder();
Address address = addressBuilder.setCity(CITY_NAME).build();
Employee.Builder builder = new Employee.Builder();
Employee employee = builder.setName("baeldung")
.setAge(10)
.setDesignation("author")
.setEmail("abc@xyz.com")
.setSupervisorName("Admin")
.setPhoneNumber(4445566)
.setPermanent(true)
.setRole("developer")
.setAddress(address)
.build();
// then
assertTrue(employee.getAddress().getCity().equalsIgnoreCase(CITY_NAME));
}
@Test
public void whenMapSalary_thenReturnEmployeeWithSalaryInUSD() {
// when
Address.Builder addressBuilder = new Address.Builder();
Address address = addressBuilder.setCity(CITY_NAME).setPinCode(PIN_CODE).build();
long salaryInEuros = INPUT_SALARY_EUROS;
Employee.Builder builder = new Employee.Builder();
Employee employee = builder
.setName("baeldung")
.setAge(10)
.setDesignation("author")
.setEmail("abc@xyz.com")
.setSupervisorName("Admin")
.setPhoneNumber(4445566)
.setPermanent(true)
.setRole("developer")
.setAddress(address)
.mapSalaryInUSD(sal -> salaryInEuros * EUROS_TO_USD_RATIO)
.build();
// then
assertTrue(employee.getAddress().getPinCode().get() == PIN_CODE);
}
@Test
public void whenOptionalFields_thenReturnEmployeeWithEmptyValues() {
// when
Address.Builder addressBuilder = new Address.Builder();
Address address = addressBuilder.setCity(CITY_NAME).build();
Employee.Builder builder = new Employee.Builder();
Employee employee = builder.setName("baeldung")
.setAge(10)
.setDesignation("author")
.setEmail("abc@xyz.com")
.setSupervisorName("Admin")
.setPhoneNumber(4445566)
.setPermanent(true)
.setRole("developer")
.setAddress(address)
.build();
// then
assertTrue(employee.getPermanent().isPresent());
assertTrue(employee.getPermanent().get());
assertFalse(employee.getDateOfJoining().isPresent());
}
@Test
public void whenNullableFields_thenReturnEmployeeWithNullValueForField() {
// when
Address.Builder addressBuilder = new Address.Builder();
Address address = addressBuilder.setCity(CITY_NAME).build();
Employee.Builder builder = new Employee.Builder();
Employee employee = builder.setName("baeldung")
.setAge(10)
.setDesignation("author")
.setEmail("abc@xyz.com")
.setSupervisorName("Admin")
.setPhoneNumber(4445566)
.setNullablePermanent(null)
.setDateOfJoining(Optional.empty())
.setRole("developer")
.setAddress(address)
.build();
// then
assertNull(employee.getCurrentProject());
}
@Test
public void whenCollectionFields_thenReturnEmployeeWithValues() {
// when
Address.Builder addressBuilder = new Address.Builder();
Address address = addressBuilder.setCity(CITY_NAME).build();
Employee.Builder builder = new Employee.Builder();
Employee employee = builder.setName("baeldung")
.setAge(10)
.setDesignation("author")
.setEmail("abc@xyz.com")
.setSupervisorName("Admin")
.setPhoneNumber(4445566)
.setNullablePermanent(null)
.setDateOfJoining(Optional.empty())
.setRole("developer")
.addAccessTokens(1221819L)
.addAccessTokens(1223441L, 134567L)
.setAddress(address)
.build();
// then
assertTrue(employee.getAccessTokens().size() == 3);
}
@Test
public void whenMapFields_thenReturnEmployeeWithValues() {
// when
Address.Builder addressBuilder = new Address.Builder();
Address address = addressBuilder.setCity(CITY_NAME).build();
Employee.Builder builder = new Employee.Builder();
Employee employee = builder.setName("baeldung")
.setAge(10)
.setDesignation("author")
.setEmail("abc@xyz.com")
.setSupervisorName("Admin")
.setPhoneNumber(4445566)
.setNullablePermanent(null)
.setDateOfJoining(Optional.empty())
.setRole("developer")
.addAccessTokens(1221819L)
.addAccessTokens(1223441L, 134567L)
.putAssetsSerialIdMapping("Laptop", 12345L)
.setAddress(address)
.build();
// then
assertTrue(employee.getAssetsSerialIdMapping().size() == 1);
}
@Test
public void whenNestedBuilderTypes_thenReturnEmployeeWithValues() {
// when
Address.Builder addressBuilder = new Address.Builder();
Address address = addressBuilder.setCity(CITY_NAME).build();
Employee.Builder builder = new Employee.Builder();
Employee employee = builder.setName("baeldung")
.setAge(10)
.setDesignation("author")
.setEmail("abc@xyz.com")
.setSupervisorName("Admin")
.setPhoneNumber(4445566)
.setNullablePermanent(null)
.setDateOfJoining(Optional.empty())
.setRole("developer")
.addAccessTokens(1221819L)
.addAccessTokens(1223441L, 134567L)
.putAssetsSerialIdMapping("Laptop", 12345L)
.setAddress(address)
.mutateAddress(a -> a.setPinCode(112200))
.build();
// then
assertTrue(employee.getAssetsSerialIdMapping().size() == 1);
}
@Test()
public void whenPartialEmployeeWithValidEmail_thenReturnEmployeeWithEmail() {
// when
Employee.Builder builder = new Employee.Builder();
Employee employee = builder.setName("baeldung")
.setAge(10)
.setEmail("abc@xyz.com")
.buildPartial();
assertNotNull(employee.getEmail());
}
}
@@ -0,0 +1,27 @@
package com.baeldung.freebuilder.builder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class EmployeeBuilderUnitTest {
public static final String NAME = "baeldung";
@Test
public void whenBuildEmployee_thenReturnValidEmployee() {
// when
Employee.Builder emplBuilder = new Employee.Builder();
Employee employee = emplBuilder
.setName(NAME)
.setAge(12)
.setDepartment("Builder Pattern")
.build();
//then
Assertions.assertTrue(employee.getName().equalsIgnoreCase(NAME));
}
}
+2 -1
View File
@@ -19,7 +19,8 @@
<module>design-patterns</module>
<module>design-patterns-2</module>
<module>solid</module>
<module>dip</module>
<module>dip</module>
<module>backoff-jitter</module>
</modules>
<dependencyManagement>