Ntry

Ntry is a small Elixir library for running operations with configurable retries and backoff.

Installation

Add ntry to your dependencies in mix.exs:

def deps do
[
{:ntry, "~> 0.1.0"}
]
end

Usage

Ntry.retry/3 accepts an operation function, a retry policy, and clauses that decide what to do with each result:

require Ntry
Ntry.retry fn -> request() end,
max_attempts: 3,
delay: 500 do
{:ok, value} -> {:halt, value}
{:error, :timeout} -> :retry
{:error, reason} -> {:halt, {:error, reason}}
end

The operation may accept a retry context. Use the :context option to bind the same context in the result clauses:

Ntry.retry fn context -> request(context.attempt) end,
max_attempts: 3,
delay: 500,
context: context,
metadata: %{service: :payments} do
{:ok, value} -> {:halt, value}
{:error, :timeout} when context.attempt < context.max_attempts -> :retry
result -> {:halt, result}
end

Ntry.Context contains:

Function API

Ntry.run/3 accepts an operation, a result handler, and a retry policy:

Ntry.run(
fn context -> request(context.attempt) end,
fn
{:ok, value}, _context -> {:halt, value}
{:error, :timeout}, _context -> :retry
result, _context -> {:halt, result}
end,
max_attempts: 3,
strategy: :exponential,
base_delay: 100,
max_delay: 2_000
)

The operation may have arity zero or one. An arity-one operation receives Ntry.Context. The handler may have arity one or two. An arity-two handler receives the result and the context.

The handler must return one of the following decisions:

If the handler requests a retry after the final attempt, Ntry returns the final operation result. Exceptions, exits, and thrown values from the operation or handler are propagated to the caller.

Policies

Available options:

A reusable policy can be supplied through :with. Options specified alongside it take precedence:

@policy [
max_attempts: 3,
strategy: :exponential,
base_delay: 100,
max_delay: 2_000
]
Ntry.retry fn -> request() end,
with: @policy,
max_attempts: 5 do
{:ok, value} -> {:halt, value}
{:error, :timeout} -> :retry
result -> {:halt, result}
end