Circuit Breaker for system protection.
Implements the Circuit Breaker pattern with three states:
:closed— Normal operation; calls are executed directly.:open— Calls are blocked immediately after exceeding the consecutive failures threshold.:half_open— Probe state after the recovery timeout. A single call is allowed to verify whether the system has recovered.
Atomic decision
The decision to allow or block execution is taken atomically inside
the GenServer (get_state_and_check), eliminating the race condition
between reading the state and executing the function.
Closing from half_open
In :half_open state, max(1, threshold / 2) consecutive successes
are required to close the circuit, mitigating the risk of premature
closure under concurrent calls.
A failure in :half_open resets the accumulated success counter on
the way back to :open, guaranteeing that the next recovery attempt
starts from zero.
Timeout measurement
System.monotonic_time/1 is used to compute the interval since the
last failure, immune to system clock adjustments (NTP, manual changes, etc.).
Registry
Each breaker is registered through Registry with a unique name under
Arrea.CircuitBreaker.Registry.
Summary
Functions
Executes a function protected by the circuit breaker.
Returns a specification to start this module under a supervisor.
Notifies the circuit breaker of an execution failure.
Returns the current state of the circuit breaker (:closed, :open, :half_open).
Starts a circuit breaker with a unique name (required in opts).
Notifies the circuit breaker of a successful execution.
Types
Functions
Executes a function protected by the circuit breaker.
- If the circuit is closed or half_open: the function is executed.
- If the circuit is open and the timeout has not elapsed: returns
{:error, :circuit_open}without executing anything. - If the breaker is not registered: the function is executed directly (behaviour equivalent to a closed circuit).
Examples
iex> CircuitBreaker.call(:my_breaker, fn -> :ok end)
{:ok, :ok}
iex> CircuitBreaker.call(:my_breaker, fn -> raise "boom" end)
{:error, :execution_failed}
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec failure(atom()) :: :ok
Notifies the circuit breaker of an execution failure.
Returns the current state of the circuit breaker (:closed, :open, :half_open).
Returns :closed if the breaker is not registered.
@spec start_link(keyword()) :: GenServer.on_start()
Starts a circuit breaker with a unique name (required in opts).
Options
:name— Unique breaker name (required):id— Alias of:name, accepted for convenience:threshold— Number of consecutive failures to open the circuit (default: 5):timeout— Time in ms before transitioning to:half_open(default: 60_000)
@spec success(atom()) :: :ok
Notifies the circuit breaker of a successful execution.