Last updated:
0 purchases
exponential back off
Retry failing processes like HTTP requests using an exponential interval between each retry #
Exponential backoff algorithm:
An exponential backoff algorithm retries requests exponentially,
increasing the waiting time between retries up to a maximum backoff time.
Features #
✅ Start process
✅ Stop process
✅ Reset Process
✅ Set max attempts
✅ Set max elapsed time
✅ Set max delay between retries
✅ Conditional retry
✅ On retry callback
✅ Tweak the exponential delay parameters
✅ Specify the amount of randomness for the delays
✅ Custom delay algorithm (inherit from the base class BackOff)
Getting started #
Add the package to your pubspec.yaml
dependencies:
exponential_back_off: ^x.y.z
copied to clipboard
Import exponential_back_off in a dart file.
import 'package:exponential_back_off/exponential_back_off.dart';
copied to clipboard
Usage #
Create ExponentialBackOff object
final exponentialBackOff = ExponentialBackOff();
copied to clipboard
Make a request
final result = await exponentialBackOff.start<Response>(
() => http.get(Uri.parse('https://www.gnu.org/')),
);
copied to clipboard
Handle the result
You can handel the result in two ways:
By checking if the result isLeft or isRight. and get the value accordingly.
Using the fold function result.fold((error){},(data){}). The fold function
will call the first(Left) function if the result is error otherwise will call second
function(Right) if the result is data.
The error will always be in Left and the data will always be in Right
Using if check:
result.fold(
(error) {
//Left(Exception): handel the error
print(error);
},
(response) {
//Right(Response): handel the result
print(response.body);
},
);
copied to clipboard
Using fold:
result.fold(
(error) {
//Left(Exception): handel the error
print(error);
},
(response) {
//Right(Response): handel the result
print(response.body);
},
);
copied to clipboard
NOTE
With the default configuration it will be retried up-to 10 times,
sleeping 1st, 2nd, 3rd, ..., 9th attempt: (will not sleep the 10th)
randomPercent: >=0.0% <=15%
1. 400 ms +/- (randomPercent of 400 ms)
2. 800 ms +/- (randomPercent of 800 ms)
3. 1600 ms +/- (randomPercent of 1600 ms)
4. 3200 ms +/- (randomPercent of 3200 ms)
5. 6400 ms +/- (randomPercent of 6400 ms)
6. 12800 ms +/- (randomPercent of 12800 ms)
7. 25600 ms +/- (randomPercent of 25600 ms)
8. 51200 ms +/- (randomPercent of 51200 ms)
9. 102400 ms +/- (randomPercent of 102400 ms)
10. 204800 ms +/- (randomPercent of 204800 ms) **will not sleep it**
copied to clipboard
Examples #
Because we love to see examples in the README (:
Simple use case with the default configuration:
final exponentialBackOff = ExponentialBackOff();
/// The result will be of type [Either<Exception, Response>]
final result = await exponentialBackOff.start<Response>(
// Make a request
() {
return get(Uri.parse('https://www.gnu.org/'))
.timeout(Duration(seconds: 10));
},
// Retry on SocketException or TimeoutException and other then that the process
// will stop and return with the error
retryIf: (e) => e is SocketException || e is TimeoutException,
);
/// You can handel the result in two ways
/// * By checking if the result `isLeft` or `isRight`. and get the value accordingly.
/// * Using the fold function `result.fold((error){},(data){})`. will call the
/// first(Left) function if the result is error otherwise will call second
/// function(Right) if the result is data.
///
/// The error will always be in Left and the data will always be in Right
// using if check
if (result.isLeft()) {
//Left(Exception): handel the error
final error = result.getLeftValue();
print(error);
} else {
//Right(Response): handel the result
final response = result.getRightValue();
print(response.body);
}
// using fold:
result.fold(
(error) {
//Left(Exception): handel the error
print(error);
},
(response) {
//Right(Response): handel the result
print(response.body);
},
);
copied to clipboard
Reusing the same object with the default configuration:
// reset will call stop() and reset everything to zero
await exponentialBackOff.reset();
print('interval: ' + exponentialBackOff.interval.toString());
print('max randomization factor: ' +
exponentialBackOff.maxRandomizationFactor.toString());
print('max attempts: ' + exponentialBackOff.maxAttempts.toString());
print('max delay: ' + exponentialBackOff.maxDelay.toString());
print('max elapsed time: ' + exponentialBackOff.maxElapsedTime.toString());
print('==================================================================');
await exponentialBackOff.start(
() => get(Uri.parse('https://www.gnu.org/')).timeout(
Duration.zero, // it will always throw TimeoutException
),
retryIf: (e) => e is SocketException || e is TimeoutException,
onRetry: (error) {
print('attempt: ' + exponentialBackOff.attemptCounter.toString());
print('error: ' + error.toString());
print('current delay: ' + exponentialBackOff.currentDelay.toString());
print('elapsed time: ' + exponentialBackOff.elapsedTime.toString());
print('--------------------------------------------------------');
},
);
copied to clipboard
Tweaks the exponential delay parameters:
// Will be retried up-to 5 times,
// sleeping 1st, 2nd, 3rd, ..., 4th attempt: (will not sleep the 5th)
//
// randomPercent: 0.0%
//
// 1. 200 ms
// 2. 400 ms
// 3. 800 ms
// 4. 1600 ms
// 5. 3200 ms **will not sleep it**
final exponentialBackOff = ExponentialBackOff(
interval: Duration(milliseconds: 100),
maxAttempts: 5,
maxRandomizationFactor: 0.0,
maxDelay: Duration(seconds: 15),
);
print('interval: ' + exponentialBackOff.interval.toString());
print('max randomization factor: ' +
exponentialBackOff.maxRandomizationFactor.toString());
print('max attempts: ' + exponentialBackOff.maxAttempts.toString());
print('max delay: ' + exponentialBackOff.maxDelay.toString());
print('max elapsed time: ' + exponentialBackOff.maxElapsedTime.toString());
print('==================================================================');
await exponentialBackOff.start(
() => get(Uri.parse('https://www.gnu.org/')).timeout(
Duration.zero, // it will always throw TimeoutException
),
retryIf: (e) => e is SocketException || e is TimeoutException,
onRetry: (error) {
print('attempt: ' + exponentialBackOff.attemptCounter.toString());
print('error: ' + error.toString());
print('current delay: ' + exponentialBackOff.currentDelay.toString());
print('elapsed time: ' + exponentialBackOff.elapsedTime.toString());
print('--------------------------------------------------------');
},
);
copied to clipboard
Custom Delay
create a subclass from Backoff base class
/// linier delays:
///
/// 1. 1000 ms
/// 2. 2000 ms
/// 3. 3000 ms
/// 4. 4000 ms
/// 5. 5000 ms
class CustomDelay extends BackOff {
CustomDelay({
super.maxAttempts,
super.maxDelay,
super.maxElapsedTime,
}) : assert(maxAttempts != null || maxElapsedTime != null,
'Can not have both maxAttempts and maxElapsedTime null');
@override
Duration computeDelay(int attempt, Duration elapsedTime) {
return Duration(seconds: attempt);
}
}
copied to clipboard
Use it as you normally do
// Will be retried up-to 5 times,
// sleeping 1st, 2nd, 3rd, ..., 4th attempt: (will not sleep the 5th)
//
// 1. 1000 ms
// 2. 2000 ms
// 3. 3000 ms
// 4. 4000 ms
// 5. 5000 ms **will not sleep it**
final customDelay = CustomDelay(maxAttempts: 5);
print('max attempts: ' + customDelay.maxAttempts.toString());
print('max delay: ' + customDelay.maxDelay.toString());
print('max elapsed time: ' + customDelay.maxElapsedTime.toString());
print('==================================================================');
await customDelay.start(
() => get(Uri.parse('https://www.gnu.org/')).timeout(
Duration.zero, // it will always throw TimeoutException
),
retryIf: (e) => e is SocketException || e is TimeoutException,
onRetry: (error) {
print('attempt: ' + customDelay.attemptCounter.toString());
print('error: ' + error.toString());
print('current delay: ' + customDelay.currentDelay.toString());
print('elapsed time: ' + customDelay.elapsedTime.toString());
print('--------------------------------------------------------');
},
);
copied to clipboard
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.