Algorithm-Backoff-RetryTimeouts
view release on metacpan or search on metacpan
lib/Algorithm/Backoff/RetryTimeouts.pm view on Meta::CPAN
#pod consider_actual_delay => 1,
#pod );
#pod
#pod my ($delay, $timeout);
#pod $timeout = $retry_algo->timeout;
#pod
#pod my $is_successful = 0;
#pod while (!$is_successful) {
#pod $actionee->timeout( $timeout );
#pod $is_successful = $actionee->do_the_thing;
#pod
#pod ($delay, $timeout) = $is_successful ? $retry_algo->success : $retry_algo->failure;
#pod die "Ran out of time" if $delay == -1;
#pod sleep $delay;
#pod }
#pod
#pod =head1 DESCRIPTION
#pod
#pod This module is a subclass of L<Algorithm::Backoff::Exponential> that adds support for
#pod adjustable timeouts during each retry. This also comes with a sane set of defaults as a
#pod good baseline for most kinds of retry operations.
#pod
#pod A combination of features solves for most problems that would arise from retry operations:
#pod
#pod =over
#pod
#pod =item *
#pod
#pod B<Maximum attempts> - Forces the algorithm to give up if repeated attempts don't yield
#pod success.
#pod
#pod =item *
#pod
#pod B<Maximum duration> - Forces the algorithm to give up if no successes happen within a
#pod certain time frame.
#pod
#pod =item *
#pod
#pod B<Exponential backoff> - A C<sqrt(2)> exponential delay keeps single retries from waiting
#pod too long, while spreading out repeated retries that may fail too quickly and run out of
#pod max attempts. This also decreases the congestion that happens with repeated attempts.
#pod
#pod =item *
#pod
#pod B<Jitter> - Adding random jitter to the retry delays solves for the Thundering Herd
#pod problem.
#pod
#pod =item *
#pod
#pod B<Adjustable timeouts> - Providing an adjustable timeout after each request solves the
#pod opposite problem of exponential backoffs: slower, unresponsive errors that gobble up all
#pod of the max duration time in one go. Each new timeout is a certain percentage of the time
#pod left.
#pod
#pod =back
#pod
#pod =head2 Typical scenario
#pod
#pod Here's an example scenario of the algorithm with existing defaults:
#pod
#pod $retry_algo is created, and timer starts
#pod
#pod Initial timeout is 25s
#pod
#pod 1st attempt fails instantly
#pod
#pod $retry_algo says to wait 1.4s (±10% jitter), and use a timeout of 24.3s
#pod
#pod 2nd attempt fails instantly
#pod
#pod $retry_algo says to wait 2s (±10% jitter), and use a timeout of 23.3s
#pod
#pod 3rd attempt fails after the full 23.3s timeout
#pod
#pod $retry_algo says to not wait (since the attempt already used up the delay), and use
#pod a timeout of 11.7s
#pod
#pod 4th attempt succeeds
#pod
#pod =cut
our %SPEC = %{ dclone \%Algorithm::Backoff::Exponential::SPEC };
{
my $args = $SPEC{new}{args};
# Our defaults
$args->{consider_actual_delay}{default} = 1;
$args->{max_attempts }{default} = 8;
$args->{max_actual_duration }{default} = 50;
$args->{jitter_factor }{default} = 0.1;
$args->{initial_delay }{default} = sqrt(2);
$args->{exponent_base }{default} = sqrt(2);
# No need to require what already has a default
$args->{initial_delay}{req} = 0;
# New arguments
$args->{adjust_timeout_factor} = {
summary => 'How much of the time left to use in the adjustable timeout',
schema => ['ufloat*', between=>[0, 1]],
default => 0.5,
};
$args->{min_adjust_timeout} = {
summary => 'Minimum adjustable timeout, in seconds',
schema => 'ufloat*',
default => 5,
};
$args->{timeout_jitter_factor} = {
summary => 'How much randomness to add to the adjustable timeout',
schema => ['float*', between=>[0, 0.5]],
default => 0.1,
};
}
#pod =head1 CONSTRUCTOR
#pod
#pod The L<"new"|Algorithm::Backoff::Exponential/new> constructor takes all of the base options
#pod from L<Algorithm::Backoff::Exponential>. Some of the defaults are changed (also shown in
#pod the L</SYNOPSIS> above), but otherwise function the same way.
#pod
lib/Algorithm/Backoff/RetryTimeouts.pm view on Meta::CPAN
consider_actual_delay => 1,
);
my ($delay, $timeout);
$timeout = $retry_algo->timeout;
my $is_successful = 0;
while (!$is_successful) {
$actionee->timeout( $timeout );
$is_successful = $actionee->do_the_thing;
($delay, $timeout) = $is_successful ? $retry_algo->success : $retry_algo->failure;
die "Ran out of time" if $delay == -1;
sleep $delay;
}
=head1 DESCRIPTION
This module is a subclass of L<Algorithm::Backoff::Exponential> that adds support for
adjustable timeouts during each retry. This also comes with a sane set of defaults as a
good baseline for most kinds of retry operations.
A combination of features solves for most problems that would arise from retry operations:
=over
=item *
B<Maximum attempts> - Forces the algorithm to give up if repeated attempts don't yield
success.
=item *
B<Maximum duration> - Forces the algorithm to give up if no successes happen within a
certain time frame.
=item *
B<Exponential backoff> - A C<sqrt(2)> exponential delay keeps single retries from waiting
too long, while spreading out repeated retries that may fail too quickly and run out of
max attempts. This also decreases the congestion that happens with repeated attempts.
=item *
B<Jitter> - Adding random jitter to the retry delays solves for the Thundering Herd
problem.
=item *
B<Adjustable timeouts> - Providing an adjustable timeout after each request solves the
opposite problem of exponential backoffs: slower, unresponsive errors that gobble up all
of the max duration time in one go. Each new timeout is a certain percentage of the time
left.
=back
=head2 Typical scenario
Here's an example scenario of the algorithm with existing defaults:
$retry_algo is created, and timer starts
Initial timeout is 25s
1st attempt fails instantly
$retry_algo says to wait 1.4s (±10% jitter), and use a timeout of 24.3s
2nd attempt fails instantly
$retry_algo says to wait 2s (±10% jitter), and use a timeout of 23.3s
3rd attempt fails after the full 23.3s timeout
$retry_algo says to not wait (since the attempt already used up the delay), and use
a timeout of 11.7s
4th attempt succeeds
=head1 CONSTRUCTOR
The L<"new"|Algorithm::Backoff::Exponential/new> constructor takes all of the base options
from L<Algorithm::Backoff::Exponential>. Some of the defaults are changed (also shown in
the L</SYNOPSIS> above), but otherwise function the same way.
=over
=item * L<max_attempts|Algorithm::Backoff::Exponential/new> => I<uint> (default: 8)
=item * L<max_actual_duration|Algorithm::Backoff::Exponential/new> => I<ufloat> (default: 50)
=item * L<jitter_factor|Algorithm::Backoff::Exponential/new> => I<float> (default: 0.1)
=item * L<initial_delay|Algorithm::Backoff::Exponential/new> => I<ufloat> (default: C<sqrt(2)>)
=item * L<exponent_base|Algorithm::Backoff::Exponential/new> => I<ufloat> (default: C<sqrt(2)>)
=item * L<delay_on_success|Algorithm::Backoff::Exponential/new> => I<ufloat> (default: 0)
=item * L<min_delay|Algorithm::Backoff::Exponential/new> => I<ufloat> (default: 0)
=item * L<max_delay|Algorithm::Backoff::Exponential/new> => I<ufloat>
=item * L<consider_actual_delay|Algorithm::Backoff::Exponential/new> => I<bool> (default: 1)
=back
The following new options are added in this module:
=over
=item * adjust_timeout_factor => I<ufloat> (default: 0.5)
How much of the remaining time to use for the next attempt's timeout, as a
factor between 0 and 1.
In order to prevent a single attempt from using up all of the remaining time, an
adjustable timeout will force the attempt to only use a portion of the time. By default,
only 50% of the remaining time will be set as the next timeout value.
=item * min_adjust_timeout => I<ufloat> (default: 5)
( run in 2.034 seconds using v1.01-cache-2.11-cpan-acebb50784d )