RetryStrategies.java
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.lambda.durable.retry;
import java.time.Duration;
import software.amazon.lambda.durable.validation.ParameterValidator;
/**
* Factory class for creating common retry strategies.
*
* <p>This class provides preset retry strategies for common use cases, as well as factory methods for creating custom
* retry strategies with exponential backoff and jitter.
*/
public class RetryStrategies {
/** Preset retry strategies for common use cases. */
public static class Presets {
/**
* Default retry strategy: - 6 total attempts (1 initial + 5 retries) - Initial delay: 5 seconds - Max delay: 60
* seconds - Backoff rate: 2x - Jitter: FULL
*/
public static final RetryStrategy DEFAULT = exponentialBackoff(
6, // maxAttempts
Duration.ofSeconds(5), // initialDelay
Duration.ofSeconds(60), // maxDelay
2.0, // backoffRate
JitterStrategy.FULL // jitter
);
/** No retry strategy - fails immediately on first error. Use this for operations that should not be retried. */
public static final RetryStrategy NO_RETRY = (error, attemptNumber) -> RetryDecision.fail();
}
/**
* Creates an exponential backoff retry strategy.
*
* <p>The delay calculation follows the formula: baseDelay = min(initialDelay × backoffRate^attemptNumber, maxDelay)
*
* @param maxAttempts Maximum number of attempts (including initial attempt)
* @param initialDelay Initial delay before first retry
* @param maxDelay Maximum delay between retries
* @param backoffRate Multiplier for exponential backoff
* @param jitter Jitter strategy to apply to delays
* @return RetryStrategy implementing exponential backoff with jitter
*/
public static RetryStrategy exponentialBackoff(
int maxAttempts, Duration initialDelay, Duration maxDelay, double backoffRate, JitterStrategy jitter) {
if (maxAttempts <= 0) {
throw new IllegalArgumentException("maxAttempts must be positive");
}
ParameterValidator.validateDuration(initialDelay, "initialDelay");
ParameterValidator.validateDuration(maxDelay, "maxDelay");
if (backoffRate <= 0) {
throw new IllegalArgumentException("backoffRate must be positive");
}
return (error, attemptNumber) -> {
// Check if we've exceeded max attempts (attemptNumber is 0-based)
if (attemptNumber + 1 >= maxAttempts) {
return RetryDecision.fail();
}
// Calculate delay with exponential backoff
double initialDelaySeconds = initialDelay.toSeconds();
double maxDelaySeconds = maxDelay.toSeconds();
double baseDelay = Math.min(initialDelaySeconds * Math.pow(backoffRate, attemptNumber), maxDelaySeconds);
// Apply jitter
double delayWithJitter = jitter.apply(baseDelay);
// Round to nearest second, minimum 1
// Same rounding logic as TS SDK: https://tinyurl.com/4ntxsefu
long finalDelaySeconds = Math.max(1, Math.round(delayWithJitter));
return RetryDecision.retry(Duration.ofSeconds(finalDelaySeconds));
};
}
/**
* Creates a simple retry strategy that retries a fixed number of times with a fixed delay.
*
* @param maxAttempts Maximum number of attempts (including initial attempt)
* @param fixedDelay Fixed delay between retries
* @return RetryStrategy with fixed delay
*/
public static RetryStrategy fixedDelay(int maxAttempts, Duration fixedDelay) {
if (maxAttempts <= 0) {
throw new IllegalArgumentException("maxAttempts must be positive");
}
ParameterValidator.validateDuration(fixedDelay, "fixedDelay");
return (error, attemptNumber) -> {
if (attemptNumber + 1 >= maxAttempts) {
return RetryDecision.fail();
}
return RetryDecision.retry(fixedDelay);
};
}
}