Rexsilient.Retry (Rexsilient v1.0.0)

Copy Markdown View Source

Calls a function again when it fails.

A retry is declared in a module that uses Rexsilient:

defmodule MyApp.Resilience do
  use Rexsilient

  alias Rexsilient.Retry.Strategy

  retry :payments do
    attempts 4
    strategy Strategy.Exponential, base_delay: 100, max_delay: 5_000, jitter: :full
  end
end

and used with @retry :payments from Rexsilient.Decorator:

defmodule MyApp.Payments do
  use Rexsilient.Decorator

  @retry :payments
  def charge(order) do
    PaymentsClient.charge(order)
  end
end

or, without a decorator, with run/2:

Rexsilient.Retry.run(:payments, fn -> PaymentsClient.charge(order) end)

Settings

  • attempts - the total number of calls, the first one included. A positive integer, or :infinity to leave the limit to the strategy. Required.

  • strategy - the module that decides how long to wait before the next attempt, or to stop, and its options. Required. See Rexsilient.Retry.Strategy.

  • classify_with - the name of the functions, in the declaring module, that decide which outcomes are failures and so are retried. Defaults to classify; see Rexsilient.Classify.

Behaviour

The function is called. If its outcome is a success, it is returned. If it is a failure and attempts remain, the strategy is asked for a delay, the caller sleeps for it, and the function is called again.

When no attempts remain, or the strategy stops, the last outcome is returned unchanged: the value the function returned, or what it raised, threw or exited with, re-raised with its original stacktrace.

{:error, :circuit_open} is never retried: a circuit breaker just said the dependency is down, and retrying would only wait for the same answer. It is a failure whatever classify says, and it ends the run at once.

The delay runs in the calling process, with Process.sleep/1.

What is retried by default

The default classify counts every {:error, _} and every raise, throw and exit as a failure, so all of them are retried. That includes bugs, such as a FunctionClauseError, which then crash only after every attempt and its delay. It also includes calls that are not idempotent and may have taken effect before failing. Use classify_with to retry only the failures that are safe and worth retrying.

Summary

Types

The name a retry is declared with.

Functions

Calls func through the retry name.

Types

name()

@type name() :: atom()

The name a retry is declared with.

Functions

run(name, func)

@spec run(name(), func :: (-> term())) :: term()

Calls func through the retry name.

Returns the outcome of the last attempt; see Behaviour.

Raises ArgumentError if no retry is registered under name.

Examples

Rexsilient.Retry.run(:payments, fn -> Payments.charge(order) end)