A lightweight, composable retry library for Java.
It provides flexible retry policies, delay strategies, and functional execution model for handling transient failures in a clean and predictable way.
- Functional retry execution model
- Pluggable delay strategies (exponential backoff, jitter, etc.)
- Configurable retry policies (e.g. max attempts)
- Composable design (Policy + Delay)
- Fully testable and deterministic core
<dependency>
<groupId>io.github.eshabakhov</groupId>
<artifactId>retry</artifactId>
<version>0.0.2</version>
</dependency>Main entry point:
Retry retry = new RtFunctional(policy, delay);
String result = retry.execute(
"operation-name",
() -> {
return "success";
}
);Controls when retries stop.
Policy policy = new PcMaxAttempts(3);Meaning: retry up to 3 attempts.
Defines wait time between retries.
Delay delay = new DlExponentialBackoff(
Duration.ofSeconds(1),
2.0
);Delay delay = new DlJitter(
attempt -> Duration.ofSeconds(10),
0.5
);Adds randomness to avoid "thundering herd" problem.
Retry retry = new RtFunctional(
new PcMaxAttempts(5),
new DlJitter(
new DlExponentialBackoff(Duration.ofMillis(500), 2.0),
0.3
)
);
String response = retry.execute(
"call-service",
() -> {
return httpClient.call();
}
);Retry loop behavior:
- Execute operation
- If success → return result
- If failure:
- check policy (
allows(nextAttempt)) - sleep using delay strategy
- retry
- check policy (
- If policy rejects → throw
RetryException
All exceptions are wrapped into:
RetryExceptionInterrupted retries preserve interruption state:
Thread.currentThread().interrupt();All core components are interfaces. You can provide your own implementations
by implementing the corresponding interface and passing it to RtFunctional.
public interface Retry {
<T> T execute(String name, Callable<T> operation) throws RetryException;
}Entry point for executing operations with retry logic. Implement to provide a custom execution strategy — e.g. async retry, circuit breaker integration, or metric collection.
public final class CustomRetry implements Retry {
@Override
public <T> T execute(final String name, final Callable<T> operation) throws RetryException {
// your custom retry logic
}
}@FunctionalInterface
public interface Policy {
boolean allows(int attempt);
}Controls whether the next retry attempt is allowed. attempt starts from 1.
Implement to express any custom condition — time-based limits, error type
filtering, external circuit state, etc.
// Allow retries only within a time window
Instant deadline = Instant.now().plusSeconds(30);
Policy policy = attempt -> Instant.now().isBefore(deadline);@FunctionalInterface
public interface Delay {
Duration delayFor(int attempt);
}Defines how long to wait before the next attempt. attempt starts from 1.
Implement to provide custom wait strategies — fixed delay, linear growth,
external configuration, etc.
// Fixed 2-second delay regardless of attempt number
Delay delay = attempt -> Duration.ofSeconds(2);All three interfaces are @FunctionalInterface, so lambda expressions
are supported throughout.
mvn clean installRun tests:
mvn clean testMutation testing:
mvn clean verify -PmutationMost retry libraries are either:
- too heavy (framework-level)
- too implicit (annotations / magic)
- too coupled to HTTP clients
This library focuses on:
explicit, composable retry logic without framework dependency