File Coverage

blib/lib/Algorithm/Backoff/LIMD.pm
Criterion Covered Total %
statement 18 19 94.7
branch 3 4 75.0
condition n/a
subroutine 5 5 100.0
pod n/a
total 26 28 92.8


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