Tinkex.CircuitBreaker (Tinkex v0.3.4)
View SourcePer-endpoint circuit breaker for resilient API calls.
Implements the circuit breaker pattern to prevent cascading failures when an endpoint is experiencing issues. The circuit has three states:
- Closed: Normal operation. Requests flow through, failures are counted.
- Open: Requests are rejected immediately. After a timeout, transitions to half-open.
- Half-Open: Limited requests allowed to test if the endpoint has recovered.
Configuration
failure_threshold: Number of failures before opening circuit (default: 5)reset_timeout_ms: Time in open state before trying half-open (default: 30,000ms)half_open_max_calls: Calls allowed in half-open state (default: 1)
Usage
cb = CircuitBreaker.new("sampling-endpoint", failure_threshold: 3)
{result, cb} = CircuitBreaker.call(cb, fn ->
# Make API call
{:ok, response}
end)ETS-based Registry
For multi-process scenarios, use CircuitBreaker.Registry to store
circuit breaker state in ETS:
CircuitBreaker.Registry.call("endpoint-name", fn ->
# Make API call
end)
Summary
Functions
Check if a request should be allowed.
Execute a function through the circuit breaker.
Create a new circuit breaker.
Record a failed call.
Record a successful call.
Reset the circuit breaker to closed state.
Get the current state of the circuit breaker.
Types
@type state() :: :closed | :open | :half_open
@type t() :: %Tinkex.CircuitBreaker{ failure_count: non_neg_integer(), failure_threshold: pos_integer(), half_open_calls: non_neg_integer(), half_open_max_calls: pos_integer(), name: String.t(), opened_at: integer() | nil, reset_timeout_ms: pos_integer(), state: state() }
Functions
Check if a request should be allowed.
Returns true if the circuit is closed or half-open (and under limit).
Returns false if the circuit is open.
@spec call(t(), (-> result), keyword()) :: {result | {:error, :circuit_open}, t()} when result: term()
Execute a function through the circuit breaker.
Returns {result, updated_circuit_breaker}.
If the circuit is open, returns {:error, :circuit_open} without
executing the function.
Options
:success?- Custom function to determine if result is a success. Default:{:ok, _}is success,{:error, _}is failure.
Examples
{result, cb} = CircuitBreaker.call(cb, fn ->
Tinkex.API.Sampling.sample_async(request, opts)
end)
# Custom success classification (4xx errors don't trip breaker)
{result, cb} = CircuitBreaker.call(cb, fn ->
Tinkex.API.post("/endpoint", body, opts)
end, success?: fn
{:ok, _} -> true
{:error, %{status: status}} when status < 500 -> true
_ -> false
end)
Create a new circuit breaker.
Options
:failure_threshold- Failures before opening (default: 5):reset_timeout_ms- Open duration before half-open (default: 30,000):half_open_max_calls- Calls allowed in half-open (default: 1)
Record a failed call.
Increments failure count. Opens circuit if threshold reached.
Record a successful call.
Resets failure count. Transitions half-open to closed.
Reset the circuit breaker to closed state.
Get the current state of the circuit breaker.
Accounts for reset timeout transitions from open to half-open.