{"id":16701400,"url":"https://github.com/ferd/backoff","last_synced_at":"2025-04-04T10:08:48.971Z","repository":{"id":51174598,"uuid":"12261318","full_name":"ferd/backoff","owner":"ferd","description":"Simple exponential backoffs in Erlang","archived":false,"fork":false,"pushed_at":"2021-05-20T17:30:05.000Z","size":177,"stargazers_count":137,"open_issues_count":3,"forks_count":17,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-03-31T18:09:08.849Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ferd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-08-21T04:39:22.000Z","updated_at":"2025-03-26T05:44:24.000Z","dependencies_parsed_at":"2022-09-26T17:31:34.588Z","dependency_job_id":null,"html_url":"https://github.com/ferd/backoff","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ferd%2Fbackoff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ferd%2Fbackoff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ferd%2Fbackoff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ferd%2Fbackoff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ferd","download_url":"https://codeload.github.com/ferd/backoff/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247157283,"owners_count":20893220,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-12T18:43:54.854Z","updated_at":"2025-04-04T10:08:48.943Z","avatar_url":"https://github.com/ferd.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Backoff\n\nBackoff is an Erlang library to deal with exponential backoffs and timers to\nbe used within OTP processes when dealing with cyclical events, such as\nreconnections, or generally retrying things.\n\n# Compiling\n\n    rebar3 compile\n\n# Running Tests\n\nTests are implemented as a basic PropEr property-based test suite. Running them\nrequires getting PropEr for the project. The following command line does\neverything needed:\n\n    $ rebar3 as test proper\n\n# Modes of Operation\n\nBackoff can be used in 3 main ways:\n\n1. a simple way to calculate exponential backoffs\n2. calculating exponential backoffs with caps and state tracking\n3. using it to fire timeout events\n\n## Simple Backoffs\n\nSimple backoffs work by calling the functions `increment/1-2`. The function\nwith one argument will grow in an unbounded manner:\n\n    1\u003e backoff:increment(1).\n    2\n    2\u003e backoff:increment(backoff:increment(1)).\n    4\n    3\u003e backoff:increment(backoff:increment(backoff:increment(1))).\n    8\n\nThe version with 2 arguments specifies a ceiling to the value:\n\n    4\u003e backoff:increment(backoff:increment(backoff:increment(2))).\n    16\n    5\u003e backoff:increment(backoff:increment(backoff:increment(2)), 10).\n    10\n\n## Simple Backoffs with jitter\n\nJitter based incremental backoffs increase the back off period for each retry attempt using a randomization function that grows exponentially. They work by calling the functions `rand_increment/1-2`. The function with one argument will grow in an unbounded manner:\n\n    1\u003e backoff:rand_increment(1).\n    3\n    2\u003e backoff:rand_increment(backoff:rand_increment(1)).\n    7\n    3\u003e backoff:rand_increment(backoff:rand_increment(backoff:rand_increment(1))).\n    19\n    4\u003e backoff:rand_increment(backoff:rand_increment(backoff:rand_increment(1))).\n    14\n    5\u003e backoff:rand_increment(backoff:rand_increment(backoff:rand_increment(1))).\n    17\n\nThe version with 2 arguments specifies a ceiling to the value. If the\ndelay is close to the ceiling the new delay will also be close to the\nceiling and may be less than the previous delay.\n\n    6\u003e backoff:rand_increment(backoff:rand_increment(backoff:rand_increment(2))).\n    21\n    7\u003e backoff:rand_increment(backoff:rand_increment(backoff:rand_increment(2)), 10).\n    10\n\n## State Backoffs\n\nState backoffs keep track of the current value, the initial value, and the\nmaximal value for you. A backoff of that kind is initialized by calling\n`init(Start,Max)` and returns an opaque data type to be used with `get/1`\n(fetches the current timer value), `fail/1` (increments the value), and\n`succeed/1` (resets the value):\n\n    6\u003e B0 = backoff:init(2, 10).\n    ...\n    7\u003e {_, B1} = backoff:fail(B0).\n    {4, ...}\n    8\u003e backoff:get(B1).\n    4\n    9\u003e {_, B2} = backoff:fail(B1).\n    {8, ...}\n    10\u003e {_, B3} = backoff:fail(B2).\n    {10, ...}\n    11\u003e {_, _} = backoff:fail(B3).\n    {10, ...}\n\nAnd here we've hit the cap with the failures. Now to succeed again:\n\n    12\u003e {_, B4} = backoff:succeed(B3).\n    {2, ...}\n    13\u003e backoff:get(B4).\n    2\n\nThat way, backoffs carry all their relevant state.\n\nIf what you want are unbound exponential backoffs, you can initiate them with:\n\n    14\u003e backoff:init(Start, 'infinity').\n\nAnd still use them as usual. The increments will have no upper limit.\n\n## State Backoffs with jitter\n\nYou can enable a jitter based incremental backoff by calling `type/2`\nthat swaps the state of the backoff:\n\n    1\u003e B0 = backoff:init(2, 30).\n    {backoff,2,30,2,normal,undefined,undefined}\n    2\u003e B1 = backoff:type(B0, jitter).\n    {backoff,2,30,2,jitter,undefined,undefined}\n    3\u003e {_, B2} = backoff:fail(B1).\n    {7, ...}\n    4\u003e {_, B3} = backoff:fail(B2).\n    {12, ...}\n\nCalling `type/2` with argument `normal` will swap the backoff state back\nto its default behavior:\n\n    5\u003e B4 = backoff:type(B3, normal).\n    {backoff,2,30,12,normal,undefined,undefined}\n    6\u003e {_, B5} = backoff:fail(B4).\n    {24, ...}\n\n## Timeout Events\n\nA very common usage for exponential backoffs are with timer events, to be used\nwhen driving reconnections or retries to certain sources. Most implementations\nof this will call `erlang:start_timer(Delay, Dest, Message)` to do this, and\nre-use the same values all the time.\n\nGiven we want Backoff to carry us the whole way there, additional arguments can\nbe given to the `init` function to deal with such state and fire events\nwhenever necessary. We first initialize the backoff with `init(Start, Max,\nDest, Message)`:\n\n    1\u003e B = backoff:init(5000, 20000, self(), hello_world).\n    ...\n\nThen by entering:\n\n    2\u003e backoff:fire(B). timer:sleep(2500), flush(). timer:sleep(3000), flush().\n\nand pressing enter, the following sequence of events will unfold:\n\n    3\u003e backoff:fire(B). timer:sleep(2500), flush(). timer:sleep(3000), flush().\n    #Ref\u003c0.0.0.719\u003e\n    4\u003e timer:sleep(2500), flush(). timer:sleep(3000), flush().\n    ok\n    5\u003e timer:sleep(3000), flush().\n    Shell got {timeout,#Ref\u003c0.0.0.719\u003e,hello_world}\n    ok\n\nShowing that `backoff:fire/1` generates a new timer, and returns the timer\nreference. This reference can be manipulated with `erlang:cancel_timer(Ref)`\nand `erlang:read_timer(Ref)`.\n\nThe shell then sleeps (2000 ms), receives nothing, then sleeps some more (3000\nms) and finally receives the timeout event as a regular Erlang timeout message.\n\nDo note that Backoff will *not* track the timer references given there can be\nenough use cases with multiple timers, event cancellation, and plenty of other\nthings that can happen with them. Backoff makes it easy to fire them for\nthe right interval, but *it is not* a wrapper around Erlang timers for all\noperations.\n\n# Changelog\n\n- 1.1.6: fix compile regexes since darwin version 17.5 would be confused with OTP 17.x\n- 1.1.5: move `proper` plugin to test profile to avoid build warnings on newer Erlangs\n- 1.1.4: fix dialyzer warnings, update doc\n- 1.1.3: switch to package version of PropEr plugin to avoid mix conflicts\n- 1.1.2: eliminate compilation warnings\n- 1.1.1: corrections to incremental backoff\n- 1.1.0: added jitter based incremental backoff\n- 1.0.0: initial commit stable for over a year\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fferd%2Fbackoff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fferd%2Fbackoff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fferd%2Fbackoff/lists"}