Algorithm-Retry

 view release on metacpan or  search on metacpan

lib/Algorithm/Retry.pm  view on Meta::CPAN

        schema => ['float*', between=>[0, 0.5]],
        description => <<'_',

If you set this to a value larger than 0, the actual delay will be between a
random number between original_delay * (1-jitter_factor) and original_delay *
(1+jitter_factor). Jitters are usually added to avoid so-called "thundering
herd" problem.

_
    },
);

our %attr_delay_on_success = (
    delay_on_success => {
        summary => 'Number of seconds to wait after a success',
        schema => 'ufloat*',
        default => 0,
    },
);

our %attr_max_delay = (
    max_delay => {
        summary => 'Maximum delay time, in seconds',
        schema => 'ufloat*',
    },
);

$SPEC{new} = {
    v => 1.1,
    is_class_meth => 1,
    is_func => 0,
    args => {
        %attr_max_attempts,
        %attr_jitter_factor,
    },
    result_naked => 1,
    result => {
        schema => 'obj*',
    },
};
sub new {
    my ($class, %args) = @_;

    my $attrspec = ${"$class\::SPEC"}{new}{args};

    # check known attributes
    for my $arg (keys %args) {
        $attrspec->{$arg} or die "$class: Unknown attribute '$arg'";
    }
    # check required attributes and set default
    for my $attr (keys %$attrspec) {
        if ($attrspec->{$attr}{req}) {
            exists($args{$attr})
                or die "$class: Missing required attribute '$attr'";
        }
        if (exists $attrspec->{$attr}{default}) {
            $args{$attr} //= $attrspec->{$attr}{default};
        }
    }
    $args{_attempts} = 0;
    bless \%args, $class;
}

sub _success_or_failure {
    my ($self, $is_success, $timestamp) = @_;

    $self->{_last_timestamp} //= $timestamp;
    $timestamp >= $self->{_last_timestamp} or
        die ref($self).": Decreasing timestamp ".
        "($self->{_last_timestamp} -> $timestamp)";
    my $delay = $is_success ?
        $self->_success($timestamp) : $self->_failure($timestamp);
    $delay = $self->{max_delay}
        if defined $self->{max_delay} && $delay > $self->{max_delay};
    $delay;
}

sub _consider_actual_delay {
    my ($self, $delay, $timestamp) = @_;

    $self->{_last_delay} //= 0;
    my $actual_delay = $timestamp - $self->{_last_timestamp};
    my $new_delay = $delay + $self->{_last_delay} - $actual_delay;
    $self->{_last_delay} = $new_delay;
    $new_delay;
}

sub success {
    my ($self, $timestamp) = @_;

    $timestamp //= time();

    $self->{_attempts} = 0;

    my $delay = $self->_success_or_failure(1, $timestamp);
    $delay = $self->_consider_actual_delay($delay, $timestamp)
        if $self->{consider_actual_delay};
    $self->{_last_timestamp} = $timestamp;
    return 0 if $delay < 0;

    $self->_add_jitter($delay);
}

sub failure {
    my ($self, $timestamp) = @_;

    $timestamp //= time();

    $self->{_attempts}++;
    return -1 if $self->{max_attempts} &&
        $self->{_attempts} >= $self->{max_attempts};

    my $delay = $self->_success_or_failure(0, $timestamp);
    $delay = $self->_consider_actual_delay($delay, $timestamp)
        if $self->{consider_actual_delay};
    $self->{_last_timestamp} = $timestamp;
    return 0 if $delay < 0;

    $self->_add_jitter($delay);
}



( run in 1.602 second using v1.01-cache-2.11-cpan-56fb94df46f )