File Coverage

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