dio_retry_it 8.0.1
dio_retry_it: ^8.0.1 copied to clipboard
Retry library for Dio and Dio package made with love. By default, the request will be retried only for appropriate retryable http statuses.
Dio Retry It ๐ #
A Smarter retry interceptor for Dio with exponential backoff and full jitter based on dio_smart_retry
โจ Why Dio Retry It? #
When building production apps, network failures are inevitable. Dio Retry It automatically handles these failures with an intelligent retry strategy that protects your servers from traffic spikes and improves user experience.
๐ฏ Key Features #
- โก Smart Retry Logic - Automatically retries failed requests based on configurable rules
- ๐ Exponential Backoff with Full Jitter - Prevents thundering herd problems when servers recover
- ๐ฒ Random Delays - Avoids synchronized retry storms from thousands of clients
- ๐ FormData Support - Automatically clones FormData before retrying
- ๐ซ Per-Request Control - Disable retries for specific requests when needed
- ๐ Built-in Logging - Track retry attempts with custom log functions
- ๐ Null Safety - Fully migrated to sound null safety
- โ๏ธ Highly Configurable - Customize every aspect of the retry behavior
๐ Table of Contents #
- โจ Why Dio Retry It?
- ๐ Getting Started
- ๐ฏ How It Works
- ๐ Advanced Usage
- โ๏ธ Configuration Reference
- ๐ Migration Guide
- ๐ Performance & Best Practices
๐ Getting Started #
Installation #
Add to your pubspec.yaml:
dependencies:
dio_retry_it: ^8.0.1
Then import it:
import 'package:dio_retry_it/dio_retry_it.dart';
Basic Usage #
final dio = Dio();
// Add the retry interceptor
dio.interceptors.add(
RetryInterceptor(
dio: dio,
maxAttempts: 3, // Retry up to 3 times
baseDelay: Duration(milliseconds: 500), // Start with 500ms
maxDelay: Duration(seconds: 10), // Cap at 10 seconds
backoffFactor: 2.0, // Double the delay each attempt
logPrint: print, // Optional: log retry attempts
),
);
// Now every failed request will be automatically retried
try {
await dio.get('https://api.example.com/data');
} catch (e) {
// After all retries fail, the error will be thrown
print('Request failed after retries: $e');
}
๐ฏ How It Works #
The Retry Strategy #
Unlike simple fixed-delay retries, Dio Retry It uses exponential backoff with full jitter - the gold standard for distributed systems.
The Algorithm:
- Calculate exponential delay:
baseDelay ร backoffFactor^(attempt-1) - Cap at
maxDelay - Apply full jitter: pick a random delay between
0and the capped value
Example with defaults (baseDelay: 500ms, backoffFactor: 2.0, maxDelay: 10s):
| Attempt | Calculation | Delay Range |
|---|---|---|
| 1 | 500ms ร 2โฐ | 0-500ms |
| 2 | 500ms ร 2ยน | 0-1,000ms |
| 3 | 500ms ร 2ยฒ | 0-2,000ms |
| 4 | 500ms ร 2ยณ | 0-4,000ms |
| 5 | 500ms ร 2โด | 0-8,000ms |
| 6+ | Capped at 10s | 0-10,000ms |
๐ก๏ธ Why Full Jitter Matters #
Imagine thousands of clients all hitting a server that's temporarily down. When the server recovers, without jitter, all clients would retry at the exact same moment, causing a massive traffic spike - effectively a DDoS attack on your own servers!
Full jitter solves this by randomizing retry times, spreading the load evenly across the recovery window.
Key Insight: Random jitter matters far more for real-world reliability than a fixed delay schedule. This is why major cloud providers like AWS and Google Cloud recommend jitter-based retries.
๐ Advanced Usage #
Custom Retry Logic #
Sometimes you need finer control over what gets retried:
final dio = Dio();
dio.interceptors.add(
RetryInterceptor(
dio: dio,
maxAttempts: 5,
retryEvaluator: (error, attempt) async {
// Only retry specific errors
if (error.type == DioExceptionType.connectionTimeout) return true;
if (error.type == DioExceptionType.receiveTimeout) return true;
// Retry server errors but not client errors
if (error.type == DioExceptionType.badResponse) {
final status = error.response?.statusCode ?? 0;
return status >= 500 && status < 600;
}
return false;
},
),
);
Custom Retry Delays #
Configure retry behavior for your specific needs:
dio.interceptors.add(
RetryInterceptor(
dio: dio,
maxAttempts: 5,
baseDelay: Duration(seconds: 1), // Start with 1 second
maxDelay: Duration(minutes: 1), // Cap at 1 minute
backoffFactor: 1.5, // Slower growth
logPrint: (msg) => debugPrint(msg),
),
);
Disable Retry for Specific Requests #
Some requests shouldn't be retried (e.g., idempotent operations):
// Using RequestOptions
final request = RequestOptions(path: '/user/delete/123')
..disableRetry = true;
await dio.fetch(request);
// Using Options
final response = await dio.get(
'https://api.example.com/status',
options: Options(extra: {'disableRetry': true}),
);
Working with FormData #
Dio Retry It automatically clones FormData before retrying - no extra work needed!
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile('image.jpg'),
'name': 'My Image',
});
// This will retry automatically if it fails
final response = await dio.post(
'https://api.example.com/upload',
data: formData,
);
โ๏ธ Configuration Reference #
Constructor Parameters #
| Parameter | Type | Default | Description |
|---|---|---|---|
dio |
Dio |
Required | The Dio instance used for retries |
maxAttempts |
int |
3 |
Maximum retry attempts (excludes original) |
baseDelay |
Duration |
500ms |
Starting delay before backoff |
maxDelay |
Duration |
10s |
Maximum delay cap |
backoffFactor |
double |
2.0 |
Multiplier per attempt (must be โฅ1) |
retryEvaluator |
RetryEvaluator? |
Default evaluator | Custom retry decision logic |
logPrint |
Function(String)? |
null |
Logging callback |
Default Retry Status Codes #
By default, responses with these status codes are retried:
- 408 - Request Timeout
- 429 - Too Many Requests
- 500 - Internal Server Error
- 502 - Bad Gateway
- 503 - Service Unavailable
- 504 - Gateway Timeout
- 440 - Login Timeout (IIS)
- 460 - Client Closed Request (AWS ELB)
- 499 - Client Closed Request (nginx)
- 520 - Web Server Unknown Error
- 521 - Web Server Is Down
- 522 - Connection Timed Out
- 523 - Origin Unreachable
- 524 - Timeout Occurred
- 525 - SSL Handshake Failed
- 527 - Railgun Error
- 598 - Network Read Timeout
- 599 - Network Connect Timeout
Extending Status Codes #
// Add your own status codes
final evaluator = DefaultRetryEvaluator(
{...defaultRetryableStatuses, 401, 403},
);
dio.interceptors.add(
RetryInterceptor(
dio: dio,
retryEvaluator: evaluator.evaluate,
),
);
๐ Migration Guide #
From dio_smart_retry to dio_retry_it #
Key Changes #
| dio_smart_retry | dio_retry_it |
|---|---|
retries: 3 |
maxAttempts: 3 |
retryDelays: [...] |
baseDelay + backoffFactor + maxDelay |
retryableExtraStatuses |
Custom retryEvaluator |
| Fixed delays | Exponential backoff with full jitter โ |
Migration Examples #
Basic Retry
Before:
RetryInterceptor(
dio: dio,
retries: 3,
retryDelays: const [
Duration(seconds: 1),
Duration(seconds: 3),
Duration(seconds: 5),
],
)
After:
RetryInterceptor(
dio: dio,
maxAttempts: 3,
baseDelay: Duration(seconds: 1),
maxDelay: Duration(seconds: 10),
backoffFactor: 2.0,
)
Custom Status Codes
Before:
RetryInterceptor(
dio: dio,
retries: 3,
retryableExtraStatuses: {401, 403},
)
After:
RetryInterceptor(
dio: dio,
maxAttempts: 3,
retryEvaluator: (error, attempt) {
if (error.type == DioExceptionType.badResponse) {
final status = error.response?.statusCode;
return status == 401 || status == 403;
}
return false;
},
)
Removed Parameters #
| Parameter | Replacement |
|---|---|
retryableExtraStatuses |
Custom retryEvaluator |
ignoreRetryEvaluatorExceptions |
Built-in error handling |
retryDelays |
baseDelay + backoffFactor |
โ Migration Checklist #
- โ Replace
retriesโmaxAttempts - โ Replace
retryDelaysโbaseDelay+backoffFactor - โ Add
maxDelayparameter - โ Update custom status codes to use
retryEvaluator - โ Remove
ignoreRetryEvaluatorExceptionsif used - โ Test your app
Benefit: New package prevents thundering herd problems with exponential backoff + full jitter - critical for production apps with many concurrent users! ๐
๐ Performance & Best Practices #
๐ฏ When to Use Retries #
โ Good Candidates:
- GET requests (idempotent)
- Network timeouts
- Temporary server errors (5xx)
- Rate limiting (429)
- Connection issues
โ Avoid Retries For:
- POST/PUT/DELETE mutations (unless idempotent)
- Client errors (4xx except 429)
- Cancelled requests
- Format/serialization errors
๐ Production Recommendations #
// Recommended production configuration
dio.interceptors.add(
RetryInterceptor(
dio: dio,
maxAttempts: 3,
baseDelay: Duration(milliseconds: 1000),
maxDelay: Duration(seconds: 30),
backoffFactor: 2.0,
logPrint: (msg) {
// Only log in development
if (kDebugMode) print(msg);
},
),
);
๐ค Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
๐ License #
Distributed under the MIT License. See LICENSE for more information.
๐ Acknowledgments #
This package is a next-generation fork of the abandoned dio_smart_retry package, rebuilt with:
- โ Modern Dart practices
- โ Full null safety
- โ Exponential backoff with full jitter
- โ Better FormData handling
- โ Improved error handling
- โ Better test coverage
๐ Support #
- ๐ GitHub Repository
- ๐ Issue Tracker
- ๐ฆ Pub.dev Package
Made with โค๏ธ for the Dart community
ยฉ 2026 Silify
