Tripwire (tripwire v0.1.0)

Copy Markdown

An OTP-native circuit breaker library for Elixir.

Tripwire wraps target GenServers with a circuit breaker pattern: consecutive failures trip the circuit to :open, which fast-fails subsequent calls. After a configurable timeout, a single probe call is allowed through to test recovery.

Usage

# Start the Registry once in your application supervisor:
{Registry, keys: :unique, name: Tripwire.Breaker.registry()}

# Start a TaskSupervisor for non-blocking forwarding:
{Task.Supervisor, name: Tripwire.TaskSupervisor}

# Start a breaker for a target:
Tripwire.start_link(target_key: :my_service, target_pid: pid)

# Call the target through the breaker:
Tripwire.call(:my_service, :some_message)

States

  • :closed - Normal operation. Calls are forwarded to the target.
  • :open - Circuit tripped. Calls are fast-failed.
  • :half_open - Recovery probe. A single call is allowed through.

Infrastructure

Tripwire requires two processes running in the supervision tree:

  • Registry named :tripwire_breakers with keys: :unique. Maps target keys to breaker PIDs.
  • Task.Supervisor named Tripwire.TaskSupervisor. Manages non-blocking task processes that forward calls to targets.

Example child specification:

children = [
  {Registry, keys: :unique, name: Tripwire.Breaker.registry()},
  {Task.Supervisor, name: Tripwire.TaskSupervisor},
  {MyApp.Dependency, [name: MyApp.Dependency]},
  {Tripwire, target_key: :dependency, target_pid: MyApp.Dependency}
]

Summary

Functions

Call a target GenServer through its circuit breaker.

Returns a child specification for including a Tripwire breaker in a supervision tree.

Start a circuit breaker for a target.

Functions

call(target_key, msg, timeout \\ 5000)

@spec call(term(), term(), timeout()) :: {:ok, term()} | {:error, term()}

Call a target GenServer through its circuit breaker.

Looks up the breaker registered for target_key via {:via, Registry, ...} addressing and forwards the call.

Parameters

  • target_key - Identifies the breaker, matching the :target_key option passed to start_link/1.
  • msg - Message forwarded to the target GenServer.
  • timeout - Maximum milliseconds to wait for a response (default 5000).

Returns

  • {:ok, result} - the target responded successfully.
  • {:error, :circuit_open} - the circuit is open. The call was not forwarded.
  • {:error, :breaker_not_registered} - no breaker registered for this key.
  • {:error, :breaker_busy} - the breaker did not respond in time.
  • {:error, {:breaker_crashed, reason}} - the breaker process crashed.

Examples

Tripwire.call(:weather, :forecast)
# => {:ok, :sunny}

Tripwire.call(:weather, :forecast, 10_000)
# => {:ok, :sunny}

Tripwire.call(:unknown, :ping)
# => {:error, :breaker_not_registered}

child_spec(opts)

@spec child_spec(keyword()) :: map()

Returns a child specification for including a Tripwire breaker in a supervision tree.

Delegates to Tripwire.Breaker.child_spec/1. The restart type is :permanent so a crashed breaker restarts at the :closed state with a failure count of zero.

Options

Same options as start_link/1.

start_link(opts)

@spec start_link(keyword()) :: GenServer.on_start()

Start a circuit breaker for a target.

The breaker is registered automatically in Registry so it can be found by call/3.

Options

  • :target_key - (required) unique key identifying this target. Used in call/3 to route calls.
  • :target_pid - (required) PID or registered name of the target GenServer.
  • :threshold - consecutive failures before tripping (default 5).
  • :reset_timeout - milliseconds before :open -> :half_open (default 60_000).

Returns

  • {:ok, pid} - breaker started and registered successfully.
  • {:error, {:already_started, pid}} - a breaker with this key is already running.
  • {:error, reason} - startup failed.