File Coverage

blib/lib/Algorithm/Backoff/Exponential.pm
Criterion Covered Total %
statement 13 13 100.0
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 18 100.0


line stmt bran cond sub pod time code
1             package Algorithm::Backoff::Exponential;
2              
3 2     2   502126 use strict;
  2         5  
  2         92  
4 2     2   12 use warnings;
  2         4  
  2         167  
5              
6 2     2   553 use parent qw(Algorithm::Backoff);
  2         389  
  2         17  
7              
8             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
9             our $DATE = '2024-02-24'; # DATE
10             our $DIST = 'Algorithm-Backoff'; # DIST
11             our $VERSION = '0.010'; # VERSION
12              
13             our %SPEC;
14              
15             $SPEC{new} = {
16             v => 1.1,
17             is_class_meth => 1,
18             is_func => 0,
19             args => {
20             %Algorithm::Backoff::attr_consider_actual_delay,
21             %Algorithm::Backoff::attr_max_actual_duration,
22             %Algorithm::Backoff::attr_max_attempts,
23             %Algorithm::Backoff::attr_jitter_factor,
24             %Algorithm::Backoff::attr_delay_on_success,
25             %Algorithm::Backoff::attr_min_delay,
26             %Algorithm::Backoff::attr_max_delay,
27             %Algorithm::Backoff::attr_initial_delay,
28             exponent_base => {
29             schema => 'ufloat*',
30             default => 2,
31             },
32             },
33             result_naked => 1,
34             result => {
35             schema => 'obj*',
36             },
37             };
38              
39             sub _success {
40 1     1   3 my ($self, $timestamp) = @_;
41 1         4 $self->{delay_on_success};
42             }
43              
44             sub _failure {
45 9     9   17 my ($self, $timestamp) = @_;
46             my $delay = $self->{initial_delay} *
47 9         36 $self->{exponent_base} ** ($self->{_attempts}-1);
48             }
49              
50             1;
51             # ABSTRACT: Backoff exponentially
52              
53             __END__
54              
55             =pod
56              
57             =encoding UTF-8
58              
59             =head1 NAME
60              
61             Algorithm::Backoff::Exponential - Backoff exponentially
62              
63             =head1 VERSION
64              
65             This document describes version 0.010 of Algorithm::Backoff::Exponential (from Perl distribution Algorithm-Backoff), released on 2024-02-24.
66              
67             =head1 SYNOPSIS
68              
69             use Algorithm::Backoff::Exponential;
70              
71             # 1. instantiate
72              
73             my $ab = Algorithm::Backoff::Exponential->new(
74             #consider_actual_delay => 1, # optional, default 0
75             #max_actual_duration => 0, # optional, default 0 (retry endlessly)
76             #max_attempts => 0, # optional, default 0 (retry endlessly)
77             #jitter_factor => 0.25, # optional, default 0
78             initial_delay => 5, # required
79             #max_delay => 100, # optional
80             #exponent_base => 2, # optional, default 2 (binary exponentiation)
81             #delay_on_success => 0, # optional, default 0
82             );
83              
84             # 2. log success/failure and get a new number of seconds to delay, timestamp is
85             # optional but must be monotonically increasing.
86              
87             # for example, using the parameters initial_delay=5, max_delay=100:
88              
89             my $secs;
90             $secs = $ab->failure(); # => 5 (= initial_delay)
91             $secs = $ab->failure(); # => 10 (5 * 2^1)
92             $secs = $ab->failure(); # => 20 (5 * 2^2)
93             $secs = $ab->failure(); # => 33 (5 * 2^3 - 7)
94             $secs = $ab->failure(); # => 80 (5 * 2^4)
95             $secs = $ab->failure(); # => 100 ( min(5 * 2^5, 100) )
96             $secs = $ab->success(); # => 0 (= delay_on_success)
97              
98             Illustration using CLI L<show-backoff-delays> (10 failures followed by 3
99             successes):
100              
101             % show-backoff-delays -a Exponential --initial-delay 1 --max-delay 200 \
102             0 0 0 0 0 0 0 0 0 0 1 1 1
103             1
104             2
105             4
106             8
107             16
108             32
109             64
110             128
111             200
112             200
113             0
114             0
115             0
116              
117             =head1 DESCRIPTION
118              
119             This backoff algorithm calculates the next delay as:
120              
121             initial_delay * exponent_base ** (attempts-1)
122              
123             Only the C<initial_delay> is required. C<exponent_base> is 2 by default (binary
124             exponential). For the first failure attempt (C<attempts> = 1) the delay equals
125             the initial delay. Then it is doubled, quadrupled, and so on (using the default
126             exponent base of 2).
127              
128             There are limits on the number of attempts (`max_attempts`) and total duration
129             (`max_actual_duration`).
130              
131             It is recommended to add a jitter factor, e.g. 0.25 to add some randomness to
132             avoid "thundering herd problem".
133              
134             =head1 METHODS
135              
136              
137             =head2 new
138              
139             Usage:
140              
141             new(%args) -> obj
142              
143             This function is not exported.
144              
145             Arguments ('*' denotes required arguments):
146              
147             =over 4
148              
149             =item * B<consider_actual_delay> => I<bool> (default: 0)
150              
151             Whether to consider actual delay.
152              
153             If set to true, will take into account the actual delay (timestamp difference).
154             For example, when using the Constant strategy of delay=2, you log failure()
155             again right after the previous failure() (i.e. specify the same timestamp).
156             failure() will then return ~2+2 = 4 seconds. On the other hand, if you waited 2
157             seconds before calling failure() again (i.e. specify the timestamp that is 2
158             seconds larger than the previous timestamp), failure() will return 2 seconds.
159             And if you waited 4 seconds or more, failure() will return 0.
160              
161             =item * B<delay_on_success> => I<ufloat> (default: 0)
162              
163             Number of seconds to wait after a success.
164              
165             =item * B<exponent_base> => I<ufloat> (default: 2)
166              
167             (No description)
168              
169             =item * B<initial_delay>* => I<ufloat>
170              
171             Initial delay for the first attempt after failure, in seconds.
172              
173             =item * B<jitter_factor> => I<float>
174              
175             How much to add randomness.
176              
177             If you set this to a value larger than 0, the actual delay will be between a
178             random number between original_delay * (1-jitter_factor) and original_delay *
179             (1+jitter_factor). Jitters are usually added to avoid so-called "thundering
180             herd" problem.
181              
182             The jitter will be applied to delay on failure as well as on success.
183              
184             =item * B<max_actual_duration> => I<ufloat> (default: 0)
185              
186             Maximum number of seconds for all of the attempts (0 means unlimited).
187              
188             If set to a positive number, will limit the number of seconds for all of the
189             attempts. This setting is used to limit the amount of time you are willing to
190             spend on a task. For example, when using the Exponential strategy of
191             initial_delay=3 and max_attempts=10, the delays will be 3, 6, 12, 24, ... If
192             failures are logged according to the suggested delays, and max_actual_duration
193             is set to 21 seconds, then the third failure() will return -1 instead of 24
194             because 3+6+12 >= 21, even though max_attempts has not been exceeded.
195              
196             =item * B<max_attempts> => I<uint> (default: 0)
197              
198             Maximum number consecutive failures before giving up.
199              
200             0 means to retry endlessly without ever giving up. 1 means to give up after a
201             single failure (i.e. no retry attempts). 2 means to retry once after a failure.
202             Note that after a success, the number of attempts is reset (as expected). So if
203             max_attempts is 3, and if you fail twice then succeed, then on the next failure
204             the algorithm will retry again for a maximum of 3 times.
205              
206             =item * B<max_delay> => I<ufloat>
207              
208             Maximum delay time, in seconds.
209              
210             =item * B<min_delay> => I<ufloat> (default: 0)
211              
212             Maximum delay time, in seconds.
213              
214              
215             =back
216              
217             Return value: (obj)
218              
219             =head1 HOMEPAGE
220              
221             Please visit the project's homepage at L<https://metacpan.org/release/Algorithm-Backoff>.
222              
223             =head1 SOURCE
224              
225             Source repository is at L<https://github.com/perlancar/perl-Algorithm-Backoff>.
226              
227             =head1 SEE ALSO
228              
229             L<https://en.wikipedia.org/wiki/Exponential_backoff>
230              
231             L<Algorithm::Backoff>
232              
233             Other C<Algorithm::Backoff::*> classes.
234              
235             =head1 AUTHOR
236              
237             perlancar <perlancar@cpan.org>
238              
239             =head1 CONTRIBUTING
240              
241              
242             To contribute, you can send patches by email/via RT, or send pull requests on
243             GitHub.
244              
245             Most of the time, you don't need to build the distribution yourself. You can
246             simply modify the code, then test via:
247              
248             % prove -l
249              
250             If you want to build the distribution (e.g. to try to install it locally on your
251             system), you can install L<Dist::Zilla>,
252             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
253             L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
254             Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
255             that are considered a bug and can be reported to me.
256              
257             =head1 COPYRIGHT AND LICENSE
258              
259             This software is copyright (c) 2024, 2019 by perlancar <perlancar@cpan.org>.
260              
261             This is free software; you can redistribute it and/or modify it under
262             the same terms as the Perl 5 programming language system itself.
263              
264             =head1 BUGS
265              
266             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Algorithm-Backoff>
267              
268             When submitting a bug or request, please include a test-file or a
269             patch to an existing test-file that illustrates the bug or desired
270             feature.
271              
272             =cut