{"id":13509707,"url":"https://github.com/safwank/ElixirRetry","last_synced_at":"2025-03-30T13:32:44.428Z","repository":{"id":17143036,"uuid":"19909623","full_name":"safwank/ElixirRetry","owner":"safwank","description":"Simple Elixir macros for linear retry, exponential backoff and wait with composable delays","archived":false,"fork":false,"pushed_at":"2024-09-23T11:48:39.000Z","size":150,"stargazers_count":442,"open_issues_count":8,"forks_count":32,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-29T20:37:44.847Z","etag":null,"topics":["delay","exponential-backoff","linear-retry","retry","retry-strategies","wait"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"terkelg/awesome-creative-coding","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/safwank.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-05-18T12:05:33.000Z","updated_at":"2024-10-26T06:37:34.000Z","dependencies_parsed_at":"2024-10-22T14:17:08.647Z","dependency_job_id":null,"html_url":"https://github.com/safwank/ElixirRetry","commit_stats":{"total_commits":142,"total_committers":24,"mean_commits":5.916666666666667,"dds":0.295774647887324,"last_synced_commit":"61c3c1bc0825bebf798f97f6d484f59b4882e22b"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwank%2FElixirRetry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwank%2FElixirRetry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwank%2FElixirRetry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwank%2FElixirRetry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/safwank","download_url":"https://codeload.github.com/safwank/ElixirRetry/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246324285,"owners_count":20759105,"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":["delay","exponential-backoff","linear-retry","retry","retry-strategies","wait"],"created_at":"2024-08-01T02:01:11.818Z","updated_at":"2025-03-30T13:32:44.188Z","avatar_url":"https://github.com/safwank.png","language":"Elixir","readme":"[![Build Status](https://github.com/safwank/ElixirRetry/actions/workflows/main.yml/badge.svg)](https://github.com/safwank/ElixirRetry/actions/workflows/main.yml)\n\n# ElixirRetry\n\nSimple Elixir macros for linear retry, exponential backoff and wait with composable delays.\n\n## Installation\n\nAdd `retry` to your list of dependencies in `mix.exs`:\n\n```elixir\n  def deps do\n    [{:retry, \"~\u003e 0.18\"}]\n  end\n```\n\nEnsure `retry` is started before your application:\n\n```elixir\n  def application do\n    [applications: [:retry]]\n  end\n```\n\n## Documentation\n\nCheck out the [API reference](https://hexdocs.pm/retry/api-reference.html) for the latest documentation.\n\n## Features\n\n### Retrying\n\nThe `retry([with: _,] do: _, after: _, else: _)` macro provides a way to retry a block of code on failure with a variety of delay and give up behaviors. By default, the execution of a block is considered a failure if it returns `:error`, `{:error, _}` or raises a runtime error.\n\nBoth the values and exceptions that will be retried can be customized. To control which values will be retried, provide the `atoms` option. To control which exceptions are retried, provide the `rescue_only` option. For example:\n\n```\nretry with: ..., atoms: [:not_ok], rescue_only: [CustomError] do\n  ...\nend\n```\n\nBoth `atoms` and `rescue_only` can accept a number of different types:\n\n* An atom (for example: `:not_okay`, `SomeStruct`, or `CustomError`). In this case, the `do` block will be retried in any of the following cases:\n  * The atom itself is returned\n  * The atom is returned in the first position of a two-tuple (for example, `{:not_okay, _}`)\n  * A struct of that type is returned/raised\n* The special atom `:all`. In this case, all values/exceptions will be retried.\n* A function (for example: `fn val -\u003e String.starts_with?(val, \"ok\") end`) or partial function (for example: `fn {:error, %SomeStruct{reason: \"busy\"}} -\u003e true`). The function will be called with the return value and the `do` block will be retried if the function returns a truthy value. If the function returns a falsy value or if no function clause matches, the `do` block will not be retried.\n* A list of any of the above. The `do` block will be retried if any of the items in the list matches.\n\nThe `after` block evaluates only when the `do` block returns a valid value before timeout. On the other hand, the `else` block evaluates only when the `do` block remains erroneous after timeout. Both are optional. By default, the `else` clause will return the last erroneous value or re-raise the last exception. The default `after` clause will simply return the last successful value.\n\n#### Example -- constant backoff\n\n```elixir\nresult = retry with: constant_backoff(100) |\u003e Stream.take(10) do\n  ExternalApi.do_something # fails if other system is down\nafter\n  result -\u003e result\nelse\n  error -\u003e error\nend\n```\n\nThis example retries every 100 milliseconds and gives up after 10 attempts.\n\n#### Example -- linear backoff\n\n```elixir\nresult = retry with: linear_backoff(10, 2) |\u003e cap(1_000) |\u003e Stream.take(10) do\n  ExternalApi.do_something # fails if other system is down\nafter\n  result -\u003e result\nelse\n  error -\u003e error\nend\n```\n\nThis example increases the delay linearly with each retry, starting with 10 milliseconds, caps the delay at 1 second and gives up after 10 attempts.\n\n#### Example -- exponential backoff\n\n```elixir\nresult = retry with: exponential_backoff() |\u003e randomize |\u003e expiry(10_000), rescue_only: [TimeoutError] do\n  ExternalApi.do_something # fails if other system is down\nafter\n  result -\u003e result\nelse\n  error -\u003e error\nend\n```\n\n#### Example -- optional clauses\n\n```elixir\nresult = retry with: constant_backoff(100) |\u003e Stream.take(10) do\n  ExternalApi.do_something # fails if other system is down\nend\n```\n\nThis example is equivalent to:\n\n```elixir\nresult = retry with: constant_backoff(100) |\u003e Stream.take(10) do\n  ExternalApi.do_something # fails if other system is down\nafter\n  result -\u003e result\nelse\n  e when is_exception(e) -\u003e raise e\n  e -\u003e e\nend\n```\n\n#### Example -- retry annotation\n\n```elixir\nuse Retry.Annotation\n\n@retry with: constant_backoff(100) |\u003e Stream.take(10)\ndef some_func(arg) do\n  ExternalApi.do_something # fails if other system is down\nend\n```\n\nThis example shows how you can annotate a function to retry every 100 milliseconds and gives up after 10 attempts.\n\n#### Delay streams\n\nThe `with:` option of `retry` accepts any `Stream` that yields integers. These integers will be interpreted as the amount of time to delay before retrying a failed operation. When the stream is exhausted `retry` will give up, returning the last value of the block.\n\n##### Example\n\n```elixir\nresult = retry with: Stream.cycle([500]) do\n  ExternalApi.do_something # fails if other system is down\nafter\n  result -\u003e result\nelse\n  error -\u003e error  \nend\n```\n\nThis will retry failures forever, waiting 0.5 seconds between attempts.\n\n`Retry.DelayStreams` provides a set of fully composable helper functions for building useful delay behaviors such as the ones in previous examples. See the `Retry.DelayStreams` module docs for full details and addition behavior not covered here. For convenience these functions are imported by `use Retry` so you can, usually, use them without prefixing them with the module name.\n\n### Waiting\n\nSimilar to `retry(with: _, do: _)`, the `wait(delay_stream, do: _, after: _, else: _)` macro provides a way to wait for a block of code to be truthy with a variety of delay and give up behaviors. The execution of a block is considered a failure if it returns `false` or `nil`.\n\n```elixir\nwait constant_backoff(100) |\u003e expiry(1_000) do\n  we_there_yet?\nafter\n  _ -\u003e\n    {:ok, \"We have arrived!\"}\nelse\n  _ -\u003e\n    {:error, \"We're still on our way :(\"}\nend\n```\n\nThis example retries every 100 milliseconds and expires after 1 second.\n\nThe `after` block evaluates only when the `do` block returns a truthy value. On the other hand, the `else` block evaluates only when the `do` block remains falsy after timeout. Both are optional. By default, a success value will be returned as `{:ok, value}` and an erroneous value will be returned as `{:error, value}`.\n\nPretty nifty for those pesky asynchronous tests and building more reliable systems in general!\n","funding_links":[],"categories":["Utilities","Elixir"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsafwank%2FElixirRetry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsafwank%2FElixirRetry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsafwank%2FElixirRetry/lists"}