File Coverage

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