retryable_ex v2.0.0 Retryable

Simple code retrying without metaprogramming.

A simple example...

Retryable.retryable [on: TimeoutError, tries: 5, sleep: 2], fn ->
  SomeApi.call
end

You can configure the defaults with Mix.Config...

use Mix.Config

config :retryable_ex, :defaults,
  tries: 10,
  sleep: fn n -> :math.pow(2, n) end

You can make named configurations to be used later...

use Mix.Config

config :retryable_ex, :aws,
  message: ["timeout", ~r/throttling/i]
  tries: 5,
  sleep: 2

Retryable.retryable(:aws, fn -> make_aws_call() end)

Link to this section Summary

Types

Function to run exactly once (similar to after in a try block)

What number retry we're on. Zero based (first retry is 0)

Exception message(s) to retry on

What to retry on (exceptions or :error)

options for retryable

Time in seconds to sleep between retries

How many times to retry

Link to this section Types

Link to this type

after_fn()
after_fn() :: (() -> any())

Function to run exactly once (similar to after in a try block).

Link to this type

count()
count() :: integer()

What number retry we're on. Zero based (first retry is 0).

Link to this type

message()
message() :: String.t() | Regex.t() | [message()]

Exception message(s) to retry on.

If message is a string, then substring matching is used.

Link to this type

on()
on() :: module() | :error | [on()]

What to retry on (exceptions or :error).

Link to this type

options()
options() :: Keyword.t() | %{optional(atom()) => term()}

options for retryable.

Link to this type

sleep()
sleep() :: integer() | float() | (count() -> integer() | float())

Time in seconds to sleep between retries.

Link to this type

tries()
tries() :: integer()

How many times to retry.

Link to this section Functions

Link to this function

retryable(options \\ [], func)
retryable(options(), (() -> term())) :: term()
retryable(name :: atom(), (() -> term())) :: term()

Maybe retry some code.

Return value is that of the given function.

Options

  • on : Retry on exception or :error. Can be a list. Default [] (retry on any exception)
  • message : Only retry if exception message matches. Default [] (retry on any message)
  • tries : How many times to retry. Default 1
  • sleep : How long to sleep (in seconds) between retries. Can be a function. Default: 1
  • after : Code to run (exactly once) no matter how many retries (zero or more). Default: nil

See the types in this module for exact specifications of the options.

When :message is specified the =~ operator is used (e.g. regex or string contains).

Named config

You can set defaults and named configurations.

use Mix.Config

config :retryable_ex, :defaults,
  on: ArgumentError

config :retryable_ex, :my_config,
  tries: 10

# Both these calls have the same effect.
retryable(:my_config, fn -> ... end)
retryable([on: ArgumentError, tries: 10], fn -> ... end)

On error

When you use the [on: error] option, the following return values will cause a retry:

  • :error
  • {:error, reason}
  • {:error, reason, ...}
  • i.e. any tuple where the first element is :error

You can customize the shape that defines an error with a function:

error_shape = fn
  :error -> true
  :failure -> true
  {:error, _reason} -> true
  {:failure, _reason} -> true
  _ -> false
end

retryable([on: {:error, error_shape}], fn ->
  case SomeModue.some_function do
    :failure = result -> result # Will cause retry
    {:ok, value} = result -> result # Will not cause retry
  end
end)

Examples

Retry on specific exception(s):

retryable([on: ArgumentError], fn -> ... end)
retryable([on: [ArgumentError, ArithmeticError]], fn -> ... end)

Retry on a specific exception message(s):

retryable([message: "some substring"], fn -> ... end)
retryable([message: ~r/some regex/], fn -> ... end)
retryable([message: ["foo", ~r/bar/]], fn -> ... end)

Retry on error:

result = retryable [on: :error], fn ->
  case something() do
    {:ok, _} = result -> result # Success, don't retry
    {:error, _} = result -> result # Error, do retry
  end
end

Retry on error or exception:

result = retryable [on: [:error, ArgumentError]], fn ->
  case something() do
    {:ok, _} = result -> {:ok, result} # Success, don't retry
    {:error, _} = result -> {:error, result} # Error, do retry
  end
end

Retry with exponential backoff:

retryable [sleep: &(:math.pow(2, &1))], fn -> ... end