Skip to main content

RetryStrategy

The RetryStrategy provides automatic retry functionality for transient failures with configurable backoff strategies.

Overview​

The retry strategy automatically retries failed operations based on configurable criteria. It supports different backoff strategies, jitter, and customizable retry conditions.

class RetryStrategy<T> extends ResilienceStrategy<T> {
RetryStrategy(RetryStrategyOptions<T> options);

Future<Outcome<T>> executeCore<T>(
ResilienceCallback<T> callback,
ResilienceContext context,
ResilienceCallback<T> next,
);
}

class RetryStrategyOptions<T> {
const RetryStrategyOptions({
this.maxRetryAttempts = 3,
this.delay = const Duration(seconds: 1),
this.backoffType = DelayBackoffType.exponential,
this.useJitter = false,
this.maxDelay = const Duration(seconds: 30),
this.shouldHandle,
this.delayGenerator,
this.onRetry,
});

const RetryStrategyOptions.noDelay({
this.maxRetryAttempts = 3,
this.backoffType = DelayBackoffType.constant,
this.useJitter = false,
this.maxDelay = const Duration(seconds: 30),
this.shouldHandle,
this.delayGenerator,
this.onRetry,
});

const RetryStrategyOptions.exponentialBackoff({
this.maxRetryAttempts = 3,
this.delay = const Duration(seconds: 1),
this.useJitter = false,
this.maxDelay = const Duration(seconds: 30),
this.shouldHandle,
this.delayGenerator,
this.onRetry,
});
}

enum DelayBackoffType {
constant,
linear,
exponential,
}

RetryStrategyOptions Properties​

maxRetryAttempts​

Maximum number of retry attempts.

Type: int
Default: 3

RetryStrategyOptions(maxRetryAttempts: 5)

delay​

Base delay between retry attempts.

Type: Duration
Default: Duration(seconds: 1)

RetryStrategyOptions(delay: Duration(milliseconds: 500))

backoffType​

The type of backoff strategy to use between retries.

Type: DelayBackoffType
Default: DelayBackoffType.exponential

Values:

  • DelayBackoffType.constant - Fixed delay between retries
  • DelayBackoffType.linear - Linearly increasing delay
  • DelayBackoffType.exponential - Exponentially increasing delay
RetryStrategyOptions(backoffType: DelayBackoffType.linear)

useJitter​

Whether to add jitter to the delay to prevent thundering herd.

Type: bool
Default: false

RetryStrategyOptions(useJitter: true)

maxDelay​

Maximum delay between retries.

Type: Duration
Default: Duration(seconds: 30)

RetryStrategyOptions(maxDelay: Duration(minutes: 2))

shouldHandle​

Predicate to determine which outcomes should be retried.

Type: ShouldHandlePredicate<T>?
Default: null (retries all exceptions)

RetryStrategyOptions(
shouldHandle: (outcome) =>
outcome.hasException &&
outcome.exception is TimeoutException,
)

delayGenerator​

Custom delay generator for advanced retry logic.

Type: DelayGenerator<T>?
Default: null

RetryStrategyOptions(
delayGenerator: (context, args) {
// Custom exponential backoff with jitter
final baseDelay = args.delay.inMilliseconds;
final attempt = args.attemptNumber;
final delayMs = baseDelay * math.pow(2, attempt - 1);
final jitter = Random().nextInt(1000);
return Duration(milliseconds: delayMs.toInt() + jitter);
},
)

onRetry​

Callback invoked when a retry is about to be performed.

Type: OnRetryCallback<T>?
Default: null

RetryStrategyOptions(
onRetry: (context, args) {
print('Retry attempt ${args.attemptNumber} after ${args.delay}');
},
)

Constructors​

RetryStrategyOptions()​

Creates retry options with default exponential backoff.

final options = RetryStrategyOptions(
maxRetryAttempts: 3,
delay: Duration(seconds: 1),
backoffType: DelayBackoffType.exponential,
);

RetryStrategyOptions.noDelay()​

Creates retry options with no delay between attempts.

final options = RetryStrategyOptions.noDelay(
maxRetryAttempts: 5,
);

RetryStrategyOptions.exponentialBackoff()​

Creates retry options with exponential backoff (same as default constructor).

final options = RetryStrategyOptions.exponentialBackoff(
maxRetryAttempts: 3,
delay: Duration(seconds: 2),
useJitter: true,
);

DelayBackoffType​

constant​

Fixed delay between retry attempts.

// Always waits 1 second between retries
RetryStrategyOptions(
delay: Duration(seconds: 1),
backoffType: DelayBackoffType.constant,
)

linear​

Linearly increasing delay between retry attempts.

// Waits 1s, 2s, 3s, 4s... between retries
RetryStrategyOptions(
delay: Duration(seconds: 1),
backoffType: DelayBackoffType.linear,
)

exponential​

Exponentially increasing delay between retry attempts.

// Waits 1s, 2s, 4s, 8s... between retries
RetryStrategyOptions(
delay: Duration(seconds: 1),
backoffType: DelayBackoffType.exponential,
)

Callback Types​

ShouldHandlePredicate<T>​

typedef ShouldHandlePredicate<T> = bool Function(Outcome<T> outcome);

Determines whether an outcome should trigger a retry.

DelayGenerator<T>​

typedef DelayGenerator<T> = Duration Function(
ResilienceContext context,
DelayGeneratorArgs args,
);

Generates custom delays for retry attempts.

OnRetryCallback<T>​

typedef OnRetryCallback<T> = void Function(
ResilienceContext context,
OnRetryArgs<T> args,
);

Called before each retry attempt.

Usage Examples​

Basic Retry​

final retryStrategy = RetryStrategy(RetryStrategyOptions(
maxRetryAttempts: 3,
delay: Duration(seconds: 1),
));

final pipeline = ResiliencePipelineBuilder()
.addStrategy(retryStrategy)
.build();

Exponential Backoff with Jitter​

final retryStrategy = RetryStrategy(RetryStrategyOptions(
maxRetryAttempts: 5,
delay: Duration(milliseconds: 500),
backoffType: DelayBackoffType.exponential,
useJitter: true,
maxDelay: Duration(seconds: 30),
));

Conditional Retry​

final retryStrategy = RetryStrategy(RetryStrategyOptions(
maxRetryAttempts: 3,
shouldHandle: (outcome) {
if (!outcome.hasException) return false;

final exception = outcome.exception;
return exception is SocketException ||
exception is TimeoutException ||
(exception is HttpException &&
[502, 503, 504].contains(exception.statusCode));
},
));

Custom Delay Logic​

final retryStrategy = RetryStrategy(RetryStrategyOptions(
maxRetryAttempts: 4,
delayGenerator: (context, args) {
// Fibonacci backoff: 1, 1, 2, 3, 5, 8...
final attempt = args.attemptNumber;
final fibDelay = fibonacci(attempt) * 1000; // milliseconds
return Duration(milliseconds: fibDelay);
},
));

Retry with Monitoring​

final retryStrategy = RetryStrategy(RetryStrategyOptions(
maxRetryAttempts: 3,
delay: Duration(seconds: 1),
backoffType: DelayBackoffType.exponential,
onRetry: (context, args) {
final requestId = context.getProperty<String>('requestId');
logger.info('Retry attempt ${args.attemptNumber} for request $requestId');

// Update metrics
metrics.incrementCounter('retries', {
'attempt': args.attemptNumber.toString(),
'exception': args.outcome.exception.runtimeType.toString(),
});
},
));

No Delay Retry​

final retryStrategy = RetryStrategy(RetryStrategyOptions.noDelay(
maxRetryAttempts: 5,
shouldHandle: (outcome) =>
outcome.hasException &&
outcome.exception is TransientException,
));

Best Practices​

Choose Appropriate Backoff​

// For network calls - exponential with jitter
RetryStrategyOptions(
backoffType: DelayBackoffType.exponential,
useJitter: true,
)

// For database operations - linear backoff
RetryStrategyOptions(
backoffType: DelayBackoffType.linear,
delay: Duration(milliseconds: 100),
)

// For quick local operations - constant delay
RetryStrategyOptions(
backoffType: DelayBackoffType.constant,
delay: Duration(milliseconds: 50),
)

Set Maximum Delays​

RetryStrategyOptions(
maxDelay: Duration(seconds: 30), // Prevent excessive delays
maxRetryAttempts: 5, // Limit total attempts
)

Handle Specific Exceptions​

RetryStrategyOptions(
shouldHandle: (outcome) {
if (!outcome.hasException) return false;

final exception = outcome.exception;

// Retry transient failures only
return exception is SocketException ||
exception is TimeoutException ||
exception is HttpException && isTransientHttpError(exception);
},
)

Use Context for State​

RetryStrategyOptions(
onRetry: (context, args) {
// Track total retry time
final startTime = context.getProperty<DateTime>('startTime') ?? DateTime.now();
final elapsed = DateTime.now().difference(startTime);
context.setProperty('totalRetryTime', elapsed);
},
)