# `Rexsilient`
[🔗](https://github.com/fabiobarkoski/rexsilient/blob/v1.0.0/lib/rexsilient.ex#L1)

Rexsilient is a resilience toolkit for Elixir. Its patterns protect the calls
an application makes to its dependencies:

  * `Rexsilient.CircuitBreaker` - stops calling a dependency that keeps
    failing.

  * `Rexsilient.Retry` - calls a function again when it fails, with backoff.

  * `Rexsilient.Fallback` - replaces the outcome of a failed call with the
    result of another function.

## Declaring patterns

Patterns are declared in any module that uses `Rexsilient`. A module may
declare as many as needed, of any kind:

    defmodule MyApp.Resilience do
      use Rexsilient

      alias Rexsilient.CircuitBreaker.{Recover, Strategy}

      circuit_breaker :payments do
        strategy Strategy.Count, threshold: 5
        recover Recover.BasicTimeout,
          reset_timeout: :timer.seconds(30),
          probing_timeout: :timer.seconds(10)
      end

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

      fallback :prices do
        function :cached_price
      end

      def cached_price(sku), do: Cache.last_price(sku)
    end

Declarations are checked at compile time: a missing required setting, a
setting used outside the block it belongs to, or one set twice fails
compilation with an error that points at the offending line. The options of
each setting are checked when the declaration is registered.

The module is then passed to `Rexsilient` in your supervision tree, which
registers its declarations when it starts:

    children = [
      {Rexsilient, modules: [MyApp.Resilience]},
      MyAppWeb.Endpoint
    ]

List it before the processes that use the declarations, so they exist when
they are first used. See `child_spec/1` for the options.

## Settings

  * `circuit_breaker/2` - `strategy/2` and `recover/2`, required, and
    `classify_with/1`.

  * `retry/2` - `attempts/1` and `strategy/2`, required, and
    `classify_with/1`.

  * `fallback/2` - `function/1`, required, and `classify_with/1`.

  * `policy/2` - at least one of `fallback/1`, `retry/1` and
    `circuit_breaker/1`.

Modules and options are evaluated at compile time, so aliases and
expressions such as `:timer.seconds(30)` are allowed.

## Using patterns

The easiest way is to annotate the functions that call the dependency, with
`Rexsilient.Decorator`:

    defmodule MyApp.Payments do
      use Rexsilient.Decorator

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

Without a decorator, calls go through `Rexsilient.CircuitBreaker.run/3`,
`Rexsilient.Retry.run/2` and `Rexsilient.Fallback.run/3`.

## Classifying failures

`use Rexsilient` defines `classify/1` and `classify/2`, which decide which
outcomes count as failures for every pattern declared in the module: by
default, `:error`, `{:error, _}` and any raise, throw or exit. Override them,
or name other functions with `classify_with/1`; see `Rexsilient.Classify`.

# `attempts`
*macro* 

Sets the total number of calls a retry makes, the first one included: a
positive integer, or `:infinity` to leave the limit to the strategy.

Can only be used inside `retry/2`.

## Examples

    attempts 4

# `child_spec`

```elixir
@spec child_spec(opts :: [modules: [module()], notifiers: [module()]]) ::
  Supervisor.child_spec()
```

Returns a child specification that registers the declarations of the given
modules and starts the processes they need.

## Options

  * `:modules` - the modules that declare with `Rexsilient`. Required.
    Raises `ArgumentError` if two declarations of the same kind share a
    name.

  * `:notifiers` - the modules told what the patterns are doing; see
    `Rexsilient.Notifier`. Defaults to
    `[Rexsilient.Notifier.TelemetryNotifier]`; `[]` sends nothing.

## Examples

    children = [
      {Rexsilient, modules: [MyApp.Resilience], notifiers: [MyApp.Notifier]}
    ]

# `circuit_breaker`
*macro* 

References the circuit breaker `name` in a policy. Can only be used inside
`policy/2`.

# `circuit_breaker`
*macro* 

Declares a circuit breaker called `name`.

`name` must be an atom, unique among all breakers registered on the node.
The block sets the breaker's settings; see [Settings](#module-settings).

## Examples

    circuit_breaker :payments do
      strategy Rexsilient.CircuitBreaker.Strategy.Count, threshold: 5
      recover Rexsilient.CircuitBreaker.Recover.BasicTimeout,
        reset_timeout: :timer.seconds(30),
        probing_timeout: :timer.seconds(10)
    end

# `classify_with`
*macro* 

Sets the name of the functions, in the declaring module, that decide which
outcomes are failures, instead of `classify/1` and `classify/2`. A missing
`func_name/1` or `func_name/2` falls back to the `classify` one of the same
arity; see `Rexsilient.Classify`.

Can only be used inside `circuit_breaker/2`, `fallback/2` or `retry/2`.

## Examples

    classify_with :classify_payments

# `fallback`
*macro* 

References the fallback `name` in a policy. Can only be used inside
`policy/2`.

# `fallback`
*macro* 

Declares a fallback called `name`.

`name` must be an atom, unique among all fallbacks registered on the node.
The block sets the fallback's settings; see `Rexsilient.Fallback`.

## Examples

    fallback :prices do
      function :cached_price
    end

# `function`
*macro* 

Sets the function a fallback calls when the call fails: a name, for a
function in the declaring module, or `{module, name}` for one in another
module. It is called with the same arguments as the failed call.

Can only be used inside `fallback/2`.

## Examples

    function :cached_price

    function {MyApp.Cache, :last_price}

# `policy`
*macro* 

Declares a policy called `name`: a fallback, a retry and a circuit breaker,
each referenced by the name it is declared with, used together with
`@policy name` from `Rexsilient.Decorator`.

The block lists the declarations with `fallback/1`, `retry/1` and
`circuit_breaker/1`. Each one is optional, but at least one is required.
They always nest in the same order, whatever the order they are written in:

    fallback( retry( circuit_breaker( call ) ) )

The referenced declarations may live in any module registered with
`Rexsilient`. A name that is not declared raises `ArgumentError` when the
policy is registered.

## Examples

    policy :prices do
      fallback :prices
      retry :prices
      circuit_breaker :prices
    end

# `recover`
*macro* 

Sets how long the breaker stays open before it lets a probe call through,
and how long a probe may run before the breaker lets another one through.

`opts` are passed to `module` and only read by it. Can only be used inside
`circuit_breaker/2`.

## Examples

    recover Rexsilient.CircuitBreaker.Recover.BasicTimeout,
      reset_timeout: :timer.seconds(30),
      probing_timeout: :timer.seconds(10)

# `retry`
*macro* 

References the retry `name` in a policy. Can only be used inside
`policy/2`.

# `retry`
*macro* 

Declares a retry called `name`.

`name` must be an atom, unique among all retries registered on the node.
The block sets the retry's settings; see `Rexsilient.Retry`.

## Examples

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

# `strategy`
*macro* 

Sets the strategy of a breaker or a retry.

Inside `circuit_breaker/2`, it decides when the breaker opens; see
`Rexsilient.CircuitBreaker.Strategy`. Inside `retry/2`, it decides how long
to wait before the next attempt, or whether to stop; see
`Rexsilient.Retry.Strategy`.

`opts` are passed to `module` and only read by it. Can only be used inside
`circuit_breaker/2` or `retry/2`.

## Examples

    strategy Rexsilient.CircuitBreaker.Strategy.Count, threshold: 5

    strategy Rexsilient.Retry.Strategy.Exponential, base_delay: 100, max_delay: 5_000

---

*Consult [api-reference.md](api-reference.md) for complete listing*
