Resolves checkstyle errors for patterns starting with letter r (#1072)
* Reduces checkstyle errors in reactor * Reduces checkstyle errors in reader-writer-lock * Reduces checkstyle errors in repository * Reduces checkstyle errors in resource-acquisition-is-initialization * Reduces checkstyle errors in retry
This commit is contained in:
committed by
Ilkka Seppälä
parent
4dae1fae57
commit
9c8ad4485b
@ -27,34 +27,35 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The <em>Retry</em> pattern enables applications to handle potentially recoverable failures from
|
||||
* the environment if the business requirements and nature of the failures allow it. By retrying
|
||||
* The <em>Retry</em> pattern enables applications to handle potentially recoverable failures from
|
||||
* the environment if the business requirements and nature of the failures allow it. By retrying
|
||||
* failed operations on external dependencies, the application may maintain stability and minimize
|
||||
* negative impact on the user experience.
|
||||
* <p>
|
||||
* In our example, we have the {@link BusinessOperation} interface as an abstraction over
|
||||
* all operations that our application performs involving remote systems. The calling code should
|
||||
* remain decoupled from implementations.
|
||||
* <p>
|
||||
* {@link FindCustomer} is a business operation that looks up a customer's record and returns
|
||||
* its ID. Imagine its job is performed by looking up the customer in our local database and
|
||||
* returning its ID. We can pass {@link CustomerNotFoundException} as one of its
|
||||
* {@link FindCustomer#FindCustomer(java.lang.String, com.iluwatar.retry.BusinessException...)
|
||||
* constructor parameters} in order to simulate not finding the customer.
|
||||
* <p>
|
||||
* Imagine that, lately, this operation has experienced intermittent failures due to some weird
|
||||
* corruption and/or locking in the data. After retrying a few times the customer is found. The
|
||||
* database is still, however, expected to always be available. While a definitive solution is
|
||||
* found to the problem, our engineers advise us to retry the operation a set number
|
||||
* of times with a set delay between retries, although not too many retries otherwise the end user
|
||||
* will be left waiting for a long time, while delays that are too short will not allow the database
|
||||
* to recover from the load.
|
||||
* <p>
|
||||
* To keep the calling code as decoupled as possible from this workaround, we have implemented the
|
||||
* retry mechanism as a {@link BusinessOperation} named {@link Retry}.
|
||||
*
|
||||
*
|
||||
* <p>In our example, we have the {@link BusinessOperation} interface as an abstraction over all
|
||||
* operations that our application performs involving remote systems. The calling code should remain
|
||||
* decoupled from implementations.
|
||||
*
|
||||
* <p>{@link FindCustomer} is a business operation that looks up a customer's record and returns
|
||||
* its ID. Imagine its job is performed by looking up the customer in our local database and
|
||||
* returning its ID. We can pass {@link CustomerNotFoundException} as one of its {@link
|
||||
* FindCustomer#FindCustomer(java.lang.String, com.iluwatar.retry.BusinessException...) constructor
|
||||
* parameters} in order to simulate not finding the customer.
|
||||
*
|
||||
* <p>Imagine that, lately, this operation has experienced intermittent failures due to some weird
|
||||
* corruption and/or locking in the data. After retrying a few times the customer is found. The
|
||||
* database is still, however, expected to always be available. While a definitive solution is
|
||||
* found to the problem, our engineers advise us to retry the operation a set number of times with a
|
||||
* set delay between retries, although not too many retries otherwise the end user will be left
|
||||
* waiting for a long time, while delays that are too short will not allow the database to recover
|
||||
* from the load.
|
||||
*
|
||||
* <p>To keep the calling code as decoupled as possible from this workaround, we have implemented
|
||||
* the retry mechanism as a {@link BusinessOperation} named {@link Retry}.
|
||||
*
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
* @see <a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/retry">Retry pattern (Microsoft Azure Docs)</a>
|
||||
* @see <a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/retry">Retry pattern
|
||||
* (Microsoft Azure Docs)</a>
|
||||
*/
|
||||
public final class App {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(App.class);
|
||||
@ -62,7 +63,7 @@ public final class App {
|
||||
|
||||
/**
|
||||
* Entry point.
|
||||
*
|
||||
*
|
||||
* @param args not used
|
||||
* @throws Exception not expected
|
||||
*/
|
||||
@ -99,22 +100,22 @@ public final class App {
|
||||
final String customerId = op.perform();
|
||||
LOG.info(String.format(
|
||||
"However, retrying the operation while ignoring a recoverable error will eventually yield "
|
||||
+ "the result %s after a number of attempts %s", customerId, retry.attempts()
|
||||
+ "the result %s after a number of attempts %s", customerId, retry.attempts()
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
private static void errorWithRetryExponentialBackoff() throws Exception {
|
||||
final RetryExponentialBackoff<String> retry = new RetryExponentialBackoff<>(
|
||||
new FindCustomer("123", new CustomerNotFoundException("not found")),
|
||||
6, //6 attempts
|
||||
30000, //30 s max delay between attempts
|
||||
e -> CustomerNotFoundException.class.isAssignableFrom(e.getClass())
|
||||
);
|
||||
);
|
||||
op = retry;
|
||||
final String customerId = op.perform();
|
||||
LOG.info(String.format(
|
||||
"However, retrying the operation while ignoring a recoverable error will eventually yield "
|
||||
+ "the result %s after a number of attempts %s", customerId, retry.attempts()
|
||||
));
|
||||
"However, retrying the operation while ignoring a recoverable error will eventually yield "
|
||||
+ "the result %s after a number of attempts %s", customerId, retry.attempts()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -24,10 +24,10 @@
|
||||
package com.iluwatar.retry;
|
||||
|
||||
/**
|
||||
* The top-most type in our exception hierarchy that signifies that an unexpected error
|
||||
* condition occurred. Its use is reserved as a "catch-all" for cases where no other subtype
|
||||
* captures the specificity of the error condition in question. Calling code is not expected to
|
||||
* be able to handle this error and should be reported to the maintainers immediately.
|
||||
* The top-most type in our exception hierarchy that signifies that an unexpected error condition
|
||||
* occurred. Its use is reserved as a "catch-all" for cases where no other subtype captures the
|
||||
* specificity of the error condition in question. Calling code is not expected to be able to handle
|
||||
* this error and should be reported to the maintainers immediately.
|
||||
*
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
*/
|
||||
@ -35,8 +35,8 @@ public class BusinessException extends Exception {
|
||||
private static final long serialVersionUID = 6235833142062144336L;
|
||||
|
||||
/**
|
||||
* Ctor
|
||||
*
|
||||
* Ctor.
|
||||
*
|
||||
* @param message the error message
|
||||
*/
|
||||
public BusinessException(String message) {
|
||||
|
@ -26,18 +26,18 @@ package com.iluwatar.retry;
|
||||
/**
|
||||
* Performs some business operation.
|
||||
*
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
* @param <T> the return type
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BusinessOperation<T> {
|
||||
/**
|
||||
* Performs some business operation, returning a value {@code T} if successful, otherwise throwing
|
||||
* an exception if an error occurs.
|
||||
*
|
||||
*
|
||||
* @return the return value
|
||||
* @throws BusinessException if the operation fails. Implementations are allowed to throw more
|
||||
* specific subtypes depending on the error conditions
|
||||
* specific subtypes depending on the error conditions
|
||||
*/
|
||||
T perform() throws BusinessException;
|
||||
}
|
||||
|
@ -24,10 +24,11 @@
|
||||
package com.iluwatar.retry;
|
||||
|
||||
/**
|
||||
* Indicates that the customer was not found.
|
||||
* <p>
|
||||
* The severity of this error is bounded by its context: was the search for the customer triggered
|
||||
* by an input from some end user, or were the search parameters pulled from your database?
|
||||
* Indicates that the customer was not found.
|
||||
*
|
||||
* <p>The severity of this error is bounded by its context: was the search for the customer
|
||||
* triggered by an input from some end user, or were the search parameters pulled from your
|
||||
* database?
|
||||
*
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
*/
|
||||
@ -36,7 +37,7 @@ public final class CustomerNotFoundException extends BusinessException {
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
*
|
||||
* @param message the error message
|
||||
*/
|
||||
public CustomerNotFoundException(String message) {
|
||||
|
@ -33,7 +33,7 @@ public final class DatabaseNotAvailableException extends BusinessException {
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
*
|
||||
* @param message the error message
|
||||
*/
|
||||
public DatabaseNotAvailableException(String message) {
|
||||
|
@ -29,9 +29,9 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Finds a customer, returning its ID from our records.
|
||||
* <p>
|
||||
* This is an imaginary operation that, for some imagined input, returns the ID for a customer.
|
||||
* However, this is a "flaky" operation that is supposed to fail intermittently, but for the
|
||||
*
|
||||
* <p>This is an imaginary operation that, for some imagined input, returns the ID for a customer.
|
||||
* However, this is a "flaky" operation that is supposed to fail intermittently, but for the
|
||||
* purposes of this example it fails in a programmed way depending on the constructor parameters.
|
||||
*
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
@ -42,15 +42,15 @@ public final class FindCustomer implements BusinessOperation<String> {
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
*
|
||||
* @param customerId the final result of the remote operation
|
||||
* @param errors the errors to throw before returning {@code customerId}
|
||||
* @param errors the errors to throw before returning {@code customerId}
|
||||
*/
|
||||
public FindCustomer(String customerId, BusinessException... errors) {
|
||||
this.customerId = customerId;
|
||||
this.errors = new ArrayDeque<>(List.of(errors));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String perform() throws BusinessException {
|
||||
if (!this.errors.isEmpty()) {
|
||||
|
@ -33,8 +33,8 @@ import java.util.function.Predicate;
|
||||
/**
|
||||
* Decorates {@link BusinessOperation business operation} with "retry" capabilities.
|
||||
*
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
* @param <T> the remote op's return type
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
*/
|
||||
public final class Retry<T> implements BusinessOperation<T> {
|
||||
private final BusinessOperation<T> op;
|
||||
@ -46,18 +46,18 @@ public final class Retry<T> implements BusinessOperation<T> {
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param op the {@link BusinessOperation} to retry
|
||||
*
|
||||
* @param op the {@link BusinessOperation} to retry
|
||||
* @param maxAttempts number of times to retry
|
||||
* @param delay delay (in milliseconds) between attempts
|
||||
* @param delay delay (in milliseconds) between attempts
|
||||
* @param ignoreTests tests to check whether the remote exception can be ignored. No exceptions
|
||||
* will be ignored if no tests are given
|
||||
* will be ignored if no tests are given
|
||||
*/
|
||||
@SafeVarargs
|
||||
public Retry(
|
||||
BusinessOperation<T> op,
|
||||
int maxAttempts,
|
||||
long delay,
|
||||
BusinessOperation<T> op,
|
||||
int maxAttempts,
|
||||
long delay,
|
||||
Predicate<Exception>... ignoreTests
|
||||
) {
|
||||
this.op = op;
|
||||
@ -70,7 +70,7 @@ public final class Retry<T> implements BusinessOperation<T> {
|
||||
|
||||
/**
|
||||
* The errors encountered while retrying, in the encounter order.
|
||||
*
|
||||
*
|
||||
* @return the errors encountered while retrying
|
||||
*/
|
||||
public List<Exception> errors() {
|
||||
@ -79,7 +79,7 @@ public final class Retry<T> implements BusinessOperation<T> {
|
||||
|
||||
/**
|
||||
* The number of retries performed.
|
||||
*
|
||||
*
|
||||
* @return the number of retries performed
|
||||
*/
|
||||
public int attempts() {
|
||||
@ -93,7 +93,7 @@ public final class Retry<T> implements BusinessOperation<T> {
|
||||
return this.op.perform();
|
||||
} catch (BusinessException e) {
|
||||
this.errors.add(e);
|
||||
|
||||
|
||||
if (this.attempts.incrementAndGet() >= this.maxAttempts || !this.test.test(e)) {
|
||||
throw e;
|
||||
}
|
||||
@ -104,7 +104,6 @@ public final class Retry<T> implements BusinessOperation<T> {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ import java.util.function.Predicate;
|
||||
/**
|
||||
* Decorates {@link BusinessOperation business operation} with "retry" capabilities.
|
||||
*
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
* @param <T> the remote op's return type
|
||||
* @author George Aristy (george.aristy@gmail.com)
|
||||
*/
|
||||
public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
|
||||
private static final Random RANDOM = new Random();
|
||||
@ -46,20 +46,20 @@ public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
|
||||
private final Predicate<Exception> test;
|
||||
private final List<Exception> errors;
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param op the {@link BusinessOperation} to retry
|
||||
* @param maxAttempts number of times to retry
|
||||
* @param ignoreTests tests to check whether the remote exception can be ignored. No exceptions
|
||||
* will be ignored if no tests are given
|
||||
*/
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param op the {@link BusinessOperation} to retry
|
||||
* @param maxAttempts number of times to retry
|
||||
* @param ignoreTests tests to check whether the remote exception can be ignored. No exceptions
|
||||
* will be ignored if no tests are given
|
||||
*/
|
||||
@SafeVarargs
|
||||
public RetryExponentialBackoff(
|
||||
BusinessOperation<T> op,
|
||||
int maxAttempts,
|
||||
long maxDelay,
|
||||
Predicate<Exception>... ignoreTests
|
||||
BusinessOperation<T> op,
|
||||
int maxAttempts,
|
||||
long maxDelay,
|
||||
Predicate<Exception>... ignoreTests
|
||||
) {
|
||||
this.op = op;
|
||||
this.maxAttempts = maxAttempts;
|
||||
@ -69,20 +69,20 @@ public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
|
||||
this.errors = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* The errors encountered while retrying, in the encounter order.
|
||||
*
|
||||
* @return the errors encountered while retrying
|
||||
*/
|
||||
/**
|
||||
* The errors encountered while retrying, in the encounter order.
|
||||
*
|
||||
* @return the errors encountered while retrying
|
||||
*/
|
||||
public List<Exception> errors() {
|
||||
return Collections.unmodifiableList(this.errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of retries performed.
|
||||
*
|
||||
* @return the number of retries performed
|
||||
*/
|
||||
/**
|
||||
* The number of retries performed.
|
||||
*
|
||||
* @return the number of retries performed
|
||||
*/
|
||||
public int attempts() {
|
||||
return this.attempts.intValue();
|
||||
}
|
||||
@ -107,8 +107,7 @@ public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user