A circuit breaker implementation for Elixir
MIT License
ExBreak is a circuit breaker implementation for Elixir.
When making function calls (typically to an external service) that may fail, you may find that you want to stop making those calls for a period of time after an error threshold is hit. This package provides a way to do that.
defmodule GitHub do
@moduledoc """
Makes calls to the GitHub API
"""
@base_url "https://api.github.com"
@doc """
Make a GET request to the GitHub API.
This request is wrapped in a circuit breaker—after 10 calls that return an
error tuple (`{:error, any}`), the circuit breaker will trip, and the
function will immediately return `{:error, :circuit_breaker_tripped}` for
the next two minutes (120 seconds).
"""
def get(path, token: token) do
ExBreak.call(&do_get/2, [path, token], threshold: 10, timeout_sec: 120)
end
defp do_get(path, token) do
HTTPoison.get("#{@base_url}#{path}", authorization: "Bearer #{token}")
end
end
It is also possible to have more fine-grained control over when a circuit breaker's trip counter is incremented. For example, to ensure that only RuntimeError
s increment the counter, but not other exceptions, the match_exception
option can be used.
The match_exception
option is a function that will be called with the exception. If it returns true
, the trip counter will be incremented when an exception occurs. Otherwise, it will not.
ExBreak.call(
&do_get/2,
[path, token],
match_exception: fn
%RuntimeError{} -> true
_ -> false
end
)
Likewise, match_return
can be used to designate what return values increment the trip counter. This option is a function that is called with the return value of the function passed to call/3
. If it returns true
, the trip counter will be incremented. Otherwise, it will not.
ExBreak.call(
&do_get/2,
[path, token],
match_return: fn
{:error, :service_unavailable} -> true
_ -> false
end
)
If you need to do some sort of alerting when a breaker trips, you can use the on_trip
option, which is a function that will receive the tripped breaker as its argument. This function is called via spawn_link/1
in the breaker's process.
ExBreak.call(
&do_get/2,
[path, token],
on_trip: fn breaker ->
Logger.error("Breaker tripped after #{breaker.break_count} breaks")
end
)
If available in Hex, the package can be installed by adding ex_break
to your list of dependencies in mix.exs
:
def deps do
[
{:ex_break, "~> 0.1.0"}
]
end
When a call ExBreak.call/3
happens, the ExBreak
module asks the ExBreak.Registry
to get an ExBreak.Breaker
state agent for the function passed to call/3
. If an agent already exists, the registry returns it. Otherwise, it starts a new one using ExBreak.BreakerSupervisor
and monitors it.
If the breaker has already been tripped and the tripped state has not expired, the function passed to call/3
is never called and the value {:error, :circuit_breaker_tripped}
is returned, instead.
If, however, the breaker has not been tripped (or its tripped state has expired), the function passed to call/3
is called. If an exception is raised when calling the function, or if the return value of the function matches the pattern {:error, _}
, a counter in the breaker's internal state is incremented, and either the exception is re-raised or the return value is returned.
If the counter in the breaker's internal state meets a configured threshold, the breaker is marked as "tripped", and subsequent calls to call/3
with the same function will return {:error, :circuit_breaker_tripped}
immediately until the tripped state expires once again.
This module is an Application
which will start when you include ex_break
in your application's dependencies. Its only responsibility is to start the ExBreak.Supervisor
.
This is a Supervisor
which starts ExBreak.DynamicSupervisor
and ExBreak.Registry
.
This module is a DynamicSupervisor
that dynamically supervises ExBreak.Breaker
agents on demand as they're needed.
This module is a registry of ExBreak.Breaker
agents. When a call to ExBreak.call/3
happens, the registry finds or creates the ExBreak.Breaker
registered for the given function call and returns it to the ExBreak
module for use.
When an ExBreak.Breaker
process exits, it is de-registered.
This module is an Agent
which stores internal state about an individual circuit breaker.
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/ex_break.