PhoenixMicro.Middleware.CircuitBreaker (PhoenixMicro v1.0.0)

Copy Markdown View Source

Circuit breaker middleware for consumer message handlers.

Implements the classic three-state machine:

CLOSED (failures >= threshold) OPEN
                                     
                                   (reset_timeout elapses)
                                     
  (probe succeeds) HALF_OPEN

States

  • CLOSED — Normal operation. All messages are processed. Failures are counted per fuse. When the failure count reaches :threshold within :window_ms, the breaker trips to OPEN.

  • OPEN — Circuit is tripped. Messages are rejected immediately with {:error, :circuit_open} — no downstream calls are made. After :reset_timeout_ms, the breaker transitions to HALF_OPEN.

  • HALF_OPEN — Probe state. The next single message is allowed through as a test. If it succeeds, the breaker resets to CLOSED. If it fails, the breaker returns to OPEN and the timeout restarts.

Usage

Add to a consumer's middleware list:

defmodule MyApp.PaymentsConsumer do
  use PhoenixMicro.Consumer

  topic "payments.created"
  middleware [
    {PhoenixMicro.Middleware.CircuitBreaker,
     fuse: :payments_db,
     threshold: 5,
     window_ms: 10_000,
     reset_timeout_ms: 30_000}
  ]

  def handle(message, _ctx), do: MyApp.Repo.insert(...)
end

Fuse names

The :fuse option names the circuit — multiple consumers can share a fuse (e.g. fuse: :payments_db) so that a downstream failure opens all of them simultaneously. Defaults to the topic name.

Storage

State is kept in an ETS table (:phoenix_micro_circuit_breakers) owned by PhoenixMicro.Middleware.CircuitBreaker.Store. This process must be started in your supervision tree, which PhoenixMicro.Application does automatically.

Telemetry events

  • [:phoenix_micro, :circuit_breaker, :tripped] — breaker opened
  • [:phoenix_micro, :circuit_breaker, :reset] — breaker closed
  • [:phoenix_micro, :circuit_breaker, :rejected] — message rejected (OPEN)
  • [:phoenix_micro, :circuit_breaker, :probe] — probe sent (HALF_OPEN)