Algori...nential(3pm)

๐Ÿ“ฆ
version 0.010 ยท 5 versions
, in debian trixie, ubuntu resolute, ubuntu stonking

๐Ÿ’– NAME ๐Ÿ’–

Algorithm::Backoff::Exponential โ€” bestie, it backs off exponentially. That's the whole vibe. It's a Perl module (you'll find it filed under 3pm, the User Contributed Perl Documentation manual, which is why the header is all squished like Algori...nential(3pm) โ€” the terminal ate the middle of her name ๐Ÿ˜ญ).


๐ŸŽ€ VERSION ๐ŸŽ€

This doc is describing version 0.010 of Algorithm::Backoff::Exponential, which ships in the Perl distribution Algorithm-Backoff, released on 2024-02-24. Yes she's got a birthday, and yes she's thriving. ๐ŸŽ‚


โœจ SYNOPSIS โœจ

Okay so first you instantiate her, then you feed her successes and failures and she hands you back a number of seconds to wait. Like a little delay oracle. ๐Ÿ”ฎ

use Algorithm::Backoff::Exponential;

# 1. instantiate

my $ab = Algorithm::Backoff::Exponential->new(
    #consider_actual_delay => 1, # optional, default 0
    #max_actual_duration   => 0, # optional, default 0 (retry endlessly)
    #max_attempts          => 0, # optional, default 0 (retry endlessly)
    #jitter_factor         => 0.25, # optional, default 0
    initial_delay          => 5, # required
    #max_delay             => 100, # optional
    #exponent_base         => 2, # optional, default 2 (binary exponentiation)
    #delay_on_success      => 0, # optional, default 0
);

# 2. log success/failure and get a new number of seconds to delay, timestamp is
# optional but must be monotonically increasing.

# for example, using the parameters initial_delay=5, max_delay=100:

my $secs;
$secs = $ab->failure();   # =>  5 (= initial_delay)
$secs = $ab->failure();   # => 10 (5 * 2^1)
$secs = $ab->failure();   # => 20 (5 * 2^2)
$secs = $ab->failure();   # => 33 (5 * 2^3 - 7)
$secs = $ab->failure();   # => 80 (5 * 2^4)
$secs = $ab->failure();   # => 100 ( min(5 * 2^5, 100) )
$secs = $ab->success();   # => 0 (= delay_on_success)

And here's her serving looks from the command line via show-backoff-delays โ€” that's 10 failures followed by 3 successes:

% show-backoff-delays -a Exponential --initial-delay 1 --max-delay 200 \
    0 0 0 0 0   0 0 0 0 0   1 1 1
1
2
4
8
16
32
64
128
200
200
0
0
0

Look at her go. 1, 2, 4, 8, 16, 32, 64, 128, then she hits the 200 ceiling twice and just chills there, and the second a success rolls in she's back at 0. Character development. ๐Ÿ’…


๐ŸŒธ DESCRIPTION ๐ŸŒธ

So the way this backoff algorithm does math is:

initial_delay * exponent_base ** (attempts-1)

Only initial_delay is required โ€” everything else is optional, she's low maintenance like that. exponent_base defaults to 2 (that's the binary exponential situation). On the very first failure attempt (when attempts = 1) the delay is just the initial delay, and from there she doubles, quadruples, and keeps on climbing (with that default exponent base of 2, obviously).

There are ceilings, bestie โ€” limits on how many attempts you get (max_attempts) and how long the whole thing can drag on (max_actual_duration).

Pro tip from the docs themselves: add a jitter factor, like 0.25, to sprinkle in some randomness and dodge the "thundering herd problem". ๐Ÿ˜๐Ÿ˜๐Ÿ˜

๐Ÿ”ง METHODS ๐Ÿ”ง

new

Usage, exactly as she comes:

new(%args) -> obj

This function is not exported. So you're calling it as a method, not importing it. Noted. ๐Ÿ“

The arguments, where * means required (no skipsies):

  • consider_actual_delay => bool (default: 0) โ€” whether to consider actual delay.

If you set this one to true, she takes the actual delay into account (that's the timestamp difference). Example time: with the Constant strategy of delay=2, if you call failure() again right after the previous failure() (same timestamp, no shame), failure() returns roughly 2+2 = 4 seconds. But if you actually waited 2 seconds before calling failure() again (timestamp 2 seconds bigger than the last one), failure() returns 2 seconds. And if you waited 4 seconds or more? failure() hands you 0. Iconic behavior honestly. โœจ

  • delay_on_success => ufloat (default: 0) โ€” number of seconds to wait after a success.
  • exponent_base => ufloat (default: 2) โ€” (No description). She's mysterious, she's just vibing, the docs say nothing and that's fine. ๐Ÿ™ˆ
  • initial_delay* => ufloat โ€” the initial delay for the first attempt after a failure, in seconds. This one's required, bestie, don't forget her.
  • jitter_factor => float โ€” how much randomness to add.

If you set this to anything larger than 0, the actual delay lands somewhere in a random number between original_delay * (1-jitter_factor) and original_delay * (1+jitter_factor). Jitters get added to avoid the so-called "thundering herd" problem. And this jitter applies to the delay on failure as well as on success. Equal opportunity jitter. ๐ŸŽฒ

  • max_actual_duration => ufloat (default: 0) โ€” maximum number of seconds for all of the attempts (0 means unlimited).

If you set it to a positive number, she caps how many seconds the whole set of attempts can take. It's how you say "I'm only willing to spend this much time on this task." Example: with the Exponential strategy of initial_delay=3 and max_attempts=10, the delays go 3, 6, 12, 24, .... If failures get logged along those suggested delays and max_actual_duration is set to 21 seconds, then the third failure() returns -1 instead of 24, because 3+6+12 >= 21 โ€” even though max_attempts was never exceeded. She said "we're done here." ๐Ÿ’€

  • max_attempts => uint (default: 0) โ€” maximum number of consecutive failures before giving up.

0 means retry endlessly, forever, never giving up, the grind never stops. 1 means give up after a single failure (so, zero retry attempts). 2 means retry once after a failure. And heads up: after a success the attempt count resets (as you'd expect). So if max_attempts is 3 and you fail twice and then succeed, then on the next failure she starts the whole retry thing over again for up to 3 times. Fresh start, new era. ๐ŸŒˆ

  • max_delay => ufloat โ€” maximum delay time, in seconds.
  • min_delay => ufloat (default: 0) โ€” maximum delay time, in seconds. Yep, that's literally what the docs say, word for word โ€” "Maximum delay time, in seconds." on the min_delay entry too. I'm not touching it, I'm just reporting. ๐Ÿซถ

Return value: (obj)


๐Ÿฆ‹ HOMEPAGE ๐Ÿฆ‹

Please go visit the project's homepage, she'd love that:

<https://metacpan.org/release/Algorithm-Backoff>


โญ SOURCE โญ

The source repository lives at:

<https://github.com/perlancar/perl-Algorithm-Backoff>


๐Ÿ‘‘ SEE ALSO ๐Ÿ‘‘


๐Ÿ’œ AUTHOR ๐Ÿ’œ

perlancar <perlancar@cpan.org>


๐Ÿงš CONTRIBUTING ๐Ÿงš

Wanna contribute? You can send patches by email / via RT, or send pull requests on GitHub. She welcomes you with open arms. ๐Ÿซถ

Most of the time you do not need to build the distribution yourself. You can just modify the code and then test it like:

% prove -l

If you do want to build the distribution (say, to try installing it locally on your system), you can install Dist::Zilla, Dist::Zilla::PluginBundle::Author::PERLANCAR, Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two other Dist::Zilla- and/or Pod::Weaver plugins. Any extra steps required beyond that are considered a bug and you can report them. No cap.


This software is copyright (c) 2024, 2019 by perlancar <perlancar@cpan.org>.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Slay, legally. โš–๏ธโœจ


๐Ÿ’€ BUGS ๐Ÿ’€

Please report any bugs or feature requests on the bugtracker website:

<https://rt.cpan.org/Public/Dist/Display.html?Name=Algorithm-Backoff>

And when you submit a bug or a request, please include a test-file or a patch to an existing test-file that shows off the bug or the desired feature. Help her help you, bestie. ๐Ÿ›


And that's the whole page, mwah ๐Ÿ’… โ€” this was Algorithm::Backoff::Exponential, served to you from perl v5.40.1 on 2025-05-04, and that header up top that looked like Algori...nential(3pm) was just her name with the middle cut out, but we all know who she is. ๐ŸŽ€ Go forth and back off exponentially. ๐Ÿ’–

๐Ÿ“ฆ where this page comes from