Algorithm-Backoff-RetryTimeouts
view release on metacpan or search on metacpan
lib/Algorithm/Backoff/RetryTimeouts.pm view on Meta::CPAN
package Algorithm::Backoff::RetryTimeouts;
use utf8;
use strict;
use warnings;
use Algorithm::Backoff::Exponential;
use base qw< Algorithm::Backoff::Exponential >;
use Storable qw< dclone >;
use Time::HiRes qw< time >;
use namespace::clean;
# ABSTRACT: A backoff-style retry algorithm with adjustable timeout support
use version;
our $VERSION = 'v1.0.0'; # VERSION
#pod =head1 SYNOPSIS
#pod
#pod use Algorithm::Backoff::RetryTimeouts;
#pod
#pod my $retry_algo = Algorithm::Backoff::RetryTimeouts->new(
#pod # common adjustments (defaults shown)
#pod max_attempts => 8,
#pod max_actual_duration => 50,
#pod jitter_factor => 0.1,
#pod timeout_jitter_factor => 0.1,
#pod adjust_timeout_factor => 0.5,
#pod min_adjust_timeout => 5,
#pod
#pod # other defaults
#pod initial_delay => sqrt(2),
#pod exponent_base => sqrt(2),
#pod delay_on_success => 0,
#pod min_delay => 0,
#pod max_delay => undef,
#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
lib/Algorithm/Backoff/RetryTimeouts.pm view on Meta::CPAN
$delay = $max_delay if $delay > $max_delay;
# Re-adjust the timeout based on the final delay and min timeout setting
$timeout = ($actual_time_left - $delay) * $timeout_factor;
$timeout = $self->_add_timeout_jitter($timeout) if $self->{timeout_jitter_factor};
$timeout = $min_time if $min_time > $timeout;
$self->{_prev_delay} = $delay;
$self->{_last_timeout} = $timeout;
return ($delay, $timeout);
}
sub _add_timeout_jitter {
my ($self, $timeout) = @_;
my $jitter = $self->{timeout_jitter_factor};
return $timeout unless $timeout && $jitter;
my $min = $timeout * (1 - $jitter);
my $max = $timeout * (1 + $jitter);
return $min + ($max - $min) * rand();
}
sub _consider_actual_delay {
my $self = shift;
# See https://github.com/perlancar/perl-Algorithm-Backoff/issues/1
$self->{_last_delay} = $self->{_prev_delay} //= 0;
return $self->SUPER::_consider_actual_delay(@_);
}
sub _success_or_failure {
my ($self, $is_success, $timestamp) = @_;
# If this is the first time, the _last_timestamp should be set to the start, not
# $timestamp. This will prevent issues with the first attempt causing unnecessary
# delays (ie: waiting 1.4s after the first attempt took longer than that).
$self->{_last_timestamp} //= $self->{_start_timestamp};
my $delay = $self->SUPER::_success_or_failure($is_success, $timestamp);
return $self->_set_last_timeout($delay, $timestamp);
}
#pod =head1 SEE ALSO
#pod
#pod L<Algorithm::Backoff> - Base distro for this module
#pod
#pod =cut
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Algorithm::Backoff::RetryTimeouts - A backoff-style retry algorithm with adjustable timeout support
=head1 VERSION
version v1.0.0
=head1 SYNOPSIS
use Algorithm::Backoff::RetryTimeouts;
my $retry_algo = Algorithm::Backoff::RetryTimeouts->new(
# common adjustments (defaults shown)
max_attempts => 8,
max_actual_duration => 50,
jitter_factor => 0.1,
timeout_jitter_factor => 0.1,
adjust_timeout_factor => 0.5,
min_adjust_timeout => 5,
# other defaults
initial_delay => sqrt(2),
exponent_base => sqrt(2),
delay_on_success => 0,
min_delay => 0,
max_delay => undef,
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.
( run in 2.762 seconds using v1.01-cache-2.11-cpan-ceb78f64989 )