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)
endDeclarations 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/2andrecover/2, required, andclassify_with/1.retry/2-attempts/1andstrategy/2, required, andclassify_with/1.fallback/2-function/1, required, andclassify_with/1.policy/2- at least one offallback/1,retry/1andcircuit_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
endWithout 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.
Summary
Functions
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.
Returns a child specification that registers the declarations of the given modules and starts the processes they need.
References the circuit breaker name in a policy. Can only be used inside
policy/2.
Declares a circuit breaker called name.
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.
References the fallback name in a policy. Can only be used inside
policy/2.
Declares a fallback called name.
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.
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.
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.
References the retry name in a policy. Can only be used inside
policy/2.
Declares a retry called name.
Sets the strategy of a breaker or a retry.
Functions
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
@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 withRexsilient. Required. RaisesArgumentErrorif two declarations of the same kind share a name.:notifiers- the modules told what the patterns are doing; seeRexsilient.Notifier. Defaults to[Rexsilient.Notifier.TelemetryNotifier];[]sends nothing.
Examples
children = [
{Rexsilient, modules: [MyApp.Resilience], notifiers: [MyApp.Notifier]}
]
References the circuit breaker name in a policy. Can only be used inside
policy/2.
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.
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
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
References the fallback name in a policy. Can only be used inside
policy/2.
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
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}
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
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)
References the retry name in a policy. Can only be used inside
policy/2.
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
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