Build fault-tolerant network services
—
Comprehensive retry mechanisms with configurable delays, maximum attempts, and conditional retry logic. Supports standard retry patterns, custom backoff strategies, and predicate-based conditional retries.
Standard retry functionality with configurable maximum attempts, delays, and exception handling.
@Retry(
maxRetries = 3,
delay = 1000,
delayUnit = ChronoUnit.MILLISECONDS,
maxDuration = 30000,
durationUnit = ChronoUnit.MILLISECONDS,
jitter = 200,
jitterDelayUnit = ChronoUnit.MILLISECONDS,
retryOn = {IOException.class, TimeoutException.class},
abortOn = {SecurityException.class}
)
public ReturnType retryableMethod() throws Exception;maxRetries - Maximum number of retry attempts (default: 3)delay - Delay between retry attempts in specified units (default: 0)delayUnit - Time unit for delay (default: MILLIS)maxDuration - Maximum total time to spend retrying (default: 180000ms)durationUnit - Time unit for max duration (default: MILLIS)jitter - Random jitter added to delays (default: 200ms)jitterDelayUnit - Time unit for jitter (default: MILLIS)retryOn - Exception types that trigger retry (default: Exception.class)abortOn - Exception types that prevent retry (takes precedence over retryOn)@ApplicationScoped
public class ExternalApiService {
// Basic retry with exponential backoff
@Retry(maxRetries = 5, delay = 1000)
@ExponentialBackoff(factor = 2, maxDelay = 30000)
public String callExternalApi(String endpoint) throws IOException {
// Implementation that may fail and should be retried
return httpClient.get(endpoint);
}
// Retry with specific exceptions
@Retry(
maxRetries = 3,
retryOn = {SocketTimeoutException.class, ConnectException.class},
abortOn = {SecurityException.class, IllegalArgumentException.class}
)
public ApiResponse secureApiCall(String token, String data) {
return authenticatedHttpClient.post(data, token);
}
}Advanced retry logic based on exception types, return values, or custom predicates.
@RetryWhen(
exception = ExceptionPredicate.class,
result = ResultPredicate.class
)
public ReturnType conditionalRetryMethod();
// Predicate interfaces
class ExceptionPredicate implements Predicate<Throwable> {
public boolean test(Throwable throwable);
}
class ResultPredicate implements Predicate<Object> {
public boolean test(Object result);
}@ApplicationScoped
public class DataService {
// Retry based on custom exception logic
@RetryWhen(exception = RetryableHttpExceptionPredicate.class)
public ApiData fetchData(String id) {
return apiClient.getData(id);
}
// Retry based on return value
@RetryWhen(result = IncompleteDataPredicate.class)
public DataSet processData(String input) {
return dataProcessor.process(input);
}
}
// Custom predicates
public class RetryableHttpExceptionPredicate implements Predicate<Throwable> {
@Override
public boolean test(Throwable throwable) {
if (throwable instanceof HttpException) {
HttpException httpEx = (HttpException) throwable;
// Retry on 5xx server errors but not 4xx client errors
return httpEx.getStatusCode() >= 500;
}
return false;
}
}
public class IncompleteDataPredicate implements Predicate<Object> {
@Override
public boolean test(Object result) {
if (result instanceof DataSet) {
DataSet dataSet = (DataSet) result;
// Retry if data set is incomplete
return dataSet.isEmpty() || !dataSet.isComplete();
}
return false;
}
}Exponential delay growth between retry attempts to reduce load on failing systems.
@ExponentialBackoff(
factor = 2,
maxDelay = 60000,
maxDelayUnit = ChronoUnit.MILLISECONDS
)
public ReturnType exponentialBackoffMethod();factor - Multiplicative factor for delay growth (default: 2)maxDelay - Maximum delay between retries (default: 1 minute)maxDelayUnit - Time unit for max delay (default: MILLIS)@ApplicationScoped
public class DatabaseService {
// Database retry with exponential backoff
@Retry(maxRetries = 5, delay = 500)
@ExponentialBackoff(factor = 3, maxDelay = 30000)
public QueryResult executeQuery(String sql) throws SQLException {
return database.query(sql);
}
// Combined with circuit breaker for resilience
@Retry(maxRetries = 3)
@ExponentialBackoff()
@CircuitBreaker(requestVolumeThreshold = 10)
public void performDatabaseUpdate(UpdateQuery query) throws SQLException {
database.execute(query);
}
}Fibonacci sequence-based delays for more gradual backoff than exponential.
@FibonacciBackoff(
maxDelay = 60000,
maxDelayUnit = ChronoUnit.MILLISECONDS
)
public ReturnType fibonacciBackoffMethod();maxDelay - Maximum delay between retries (default: 1 minute)maxDelayUnit - Time unit for max delay (default: MILLIS)@ApplicationScoped
public class MessageService {
// Message queue retry with Fibonacci backoff
@Retry(maxRetries = 8, delay = 100)
@FibonacciBackoff(maxDelay = 20000)
public void sendMessage(Message message) throws MessagingException {
messageQueue.send(message);
}
}Custom backoff strategies for specialized retry delay patterns.
@CustomBackoff(CustomBackoffStrategy.class)
public ReturnType customBackoffMethod();
// Custom backoff strategy interface
interface CustomBackoffStrategy {
long nextDelayInMillis(int attemptIndex);
}@ApplicationScoped
public class SpecializedService {
// Custom backoff for API rate limiting
@Retry(maxRetries = 10)
@CustomBackoff(RateLimitBackoffStrategy.class)
public ApiResponse callRateLimitedApi(String endpoint) {
return apiClient.get(endpoint);
}
}
// Custom backoff implementation
public class RateLimitBackoffStrategy implements CustomBackoffStrategy {
private static final long[] DELAYS = {1000, 2000, 5000, 10000, 20000};
@Override
public long nextDelayInMillis(int attemptIndex) {
if (attemptIndex < DELAYS.length) {
return DELAYS[attemptIndex];
}
// For attempts beyond defined delays, use maximum delay
return DELAYS[DELAYS.length - 1];
}
}Execute custom logic before each retry attempt for logging, cleanup, or state management.
@BeforeRetry(BeforeRetryHandler.class)
public ReturnType beforeRetryMethod();
// Handler interface
interface BeforeRetryHandler {
void handle(InvocationContext context);
}
// Alternative: method-based before retry (build-time configuration)
@BeforeRetry(methodName = "handleBeforeRetry")
public ReturnType methodBasedBeforeRetry();
public void handleBeforeRetry() {
// Custom before retry logic
}@ApplicationScoped
public class MonitoredService {
@Inject
Logger logger;
@Inject
MetricsCollector metrics;
// Before retry with custom handler
@Retry(maxRetries = 3)
@BeforeRetry(RetryLoggingHandler.class)
public String monitoredApiCall(String endpoint) throws Exception {
return externalApi.call(endpoint);
}
// Before retry with method
@Retry(maxRetries = 5)
@BeforeRetry(methodName = "logRetryAttempt")
public void importantOperation() throws Exception {
performCriticalTask();
}
public void logRetryAttempt() {
logger.warn("Retrying important operation due to failure");
metrics.incrementRetryCounter("important-operation");
}
}
// Custom before retry handler
public class RetryLoggingHandler implements BeforeRetryHandler {
@Inject
Logger logger;
@Override
public void handle(InvocationContext context) {
String methodName = context.getMethod().getName();
Throwable failure = context.getFailure();
logger.info("Retrying method {} due to {}", methodName, failure.getClass().getSimpleName());
}
}// Time units for delays and durations
enum ChronoUnit {
NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS, DAYS
}
// Invocation context for before retry handlers
interface InvocationContext {
Method getMethod();
Object[] getParameters();
Object getTarget();
Throwable getFailure();
Map<String, Object> getContextData();
}
// Predicate for conditional retry
interface Predicate<T> {
boolean test(T t);
}// Custom backoff strategy interface
interface CustomBackoffStrategy {
/**
* Calculate the next delay based on attempt index
* @param attemptIndex Zero-based index of the retry attempt
* @return Delay in milliseconds before next retry
*/
long nextDelayInMillis(int attemptIndex);
}
// Before retry handler interface
interface BeforeRetryHandler {
/**
* Handle logic to execute before each retry attempt
* @param context Invocation context with method and failure information
*/
void handle(InvocationContext context);
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-smallrye-fault-tolerance