{"id":17466258,"url":"https://github.com/mingchuno/exbackoff","last_synced_at":"2025-06-16T00:03:55.442Z","repository":{"id":57499150,"uuid":"52662693","full_name":"mingchuno/exbackoff","owner":"mingchuno","description":"Simple exponential backoffs in Elixir","archived":false,"fork":false,"pushed_at":"2021-02-20T15:23:39.000Z","size":20,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-18T15:32:10.463Z","etag":null,"topics":["backoff","elixir","erlang","hex"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","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/mingchuno.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-02-27T11:04:49.000Z","updated_at":"2024-09-10T17:41:11.000Z","dependencies_parsed_at":"2022-08-28T15:20:37.223Z","dependency_job_id":null,"html_url":"https://github.com/mingchuno/exbackoff","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingchuno%2Fexbackoff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingchuno%2Fexbackoff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingchuno%2Fexbackoff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingchuno%2Fexbackoff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mingchuno","download_url":"https://codeload.github.com/mingchuno/exbackoff/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249470229,"owners_count":21277721,"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":["backoff","elixir","erlang","hex"],"created_at":"2024-10-18T13:16:35.835Z","updated_at":"2025-04-22T14:45:17.105Z","avatar_url":"https://github.com/mingchuno.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Exbackoff\n\n[![Build Status](https://travis-ci.org/mingchuno/exbackoff.svg?branch=master)](https://travis-ci.org/mingchuno/exbackoff)\n[![Hex Version](http://img.shields.io/hexpm/v/exbackoff.svg)](https://hex.pm/packages/exbackoff)\n[![Inline docs](http://inch-ci.org/github/mingchuno/exbackoff.svg?branch=master)](http://inch-ci.org/github/mingchuno/exbackoff)\n\nExbackoff 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. This is port of Erlang counterpart\nin [ferd/backoff](https://github.com/ferd/backoff).\n\n## Installation\n\n[Available in Hex](https://hex.pm/packages/exbackoff), the package can be installed as:\n\n  1. Add exbackoff to your list of dependencies in `mix.exs`:\n\n        [{:exbackoff, \"~\u003e 0.1.0\"}]\n\n  2. Ensure exbackoff is started before your application:\n\n        [applications: [:exbackoff]]\n\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 1 |\u003e Exbackoff.increment\n    2\n    2\u003e 1 |\u003e Exbackoff.increment |\u003e Exbackoff.increment\n    4\n    3\u003e 1 |\u003e Exbackoff.increment |\u003e Exbackoff.increment |\u003e Exbackoff.increment\n    8\n\nThe version with 2 arguments specifies a ceiling to the value:\n\n    4\u003e 2 |\u003e Exbackoff.increment |\u003e Exbackoff.increment |\u003e Exbackoff.increment\n    16\n    5\u003e 2 |\u003e Exbackoff.increment(10) |\u003e Exbackoff.increment |\u003e Exbackoff.increment\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 1 |\u003e Exbackoff.rand_increment\n    3\n    2\u003e 1 |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment\n    7\n    3\u003e 1 |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment\n    19\n    4\u003e 1 |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment\n    14\n    5\u003e 1 |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment\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 2 |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment\n    21\n    7\u003e 2 |\u003e Exbackoff.rand_increment(10) |\u003e Exbackoff.rand_increment |\u003e Exbackoff.rand_increment\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 = Exbackoff.init(2, 10)\n    ...\n    7\u003e {_, b1} = Exbackoff.fail(Bb)\n    {4, ...}\n    8\u003e Exbackoff.get(b1)\n    4\n    9\u003e {_, b2} = Exbackoff.fail(b1)\n    {8, ...}\n    10\u003e {_, b3} = Exbackoff.fail(b2)\n    {10, ...}\n    11\u003e {_, _} = Exbackoff.fail(b3)\n    {10, ...}\n\nAnd here we've hit the cap with the failures. Now to succeed again:\n\n    12\u003e {_, b4} = Exbackoff.succeed(b3).\n    {2, ...}\n    13\u003e Exbackoff.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 Exbackoff.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 = Exbackoff.init(2, 30)\n    {:backoff,2,30,2,:normal,nil,nil}\n    2\u003e b1 = Exbackoff.type(b0, jitter)\n    {:backoff,2,30,2,:jitter,nil,nil}\n    3\u003e {_, b2} = Exbackoff.fail(b1)\n    {7, ...}\n    4\u003e {_, b3} = Exbackoff.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 = Exbackoff.type(b3, :normal)\n    {:backoff,2,30,12,:normal,nil,nil}\n    6\u003e {_, b5} = Exbackoff.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 = Exbackoff.init(5000, 20000, self(), :hello_world).\n    ...\n\nThen by entering:\n\n    2\u003e Exbackoff.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 Exbackoff.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 `Exbackoff.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## TODO\n\n1. publish\n2. add common task in readme","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmingchuno%2Fexbackoff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmingchuno%2Fexbackoff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmingchuno%2Fexbackoff/lists"}