ExternalService.CircuitBreaker behaviour (ExternalService v2.2.0)

Copy Markdown View Source

The behaviour implemented by circuit breaker backends.

A service's breaker is chosen with the :backend circuit breaker option, and defaults to ExternalService.CircuitBreaker.Fuse — a node-local breaker built on the :fuse library.

Writing a backend

defmodule MyApp.CircuitBreaker do
  @behaviour ExternalService.CircuitBreaker

  @impl true
  def install(service, options) do
    {:ok, %{key: service, tolerate: options[:tolerate]}}
  end

  @impl true
  def ask(_service, config), do: if MyStore.open?(config.key), do: :blown, else: :ok

  @impl true
  def melt(_service, config), do: MyStore.record_failure(config.key)

  @impl true
  def reset(_service, config), do: MyStore.close(config.key)

  @impl true
  def remove(_service, config), do: MyStore.forget(config.key)
end

Backends are stateless modules. install/2 returns an opaque config term that is stored with the rest of the service state (in :persistent_term) and handed back to every other callback, so a backend needs no process, supervisor, or registry of its own.

This library never calls these functions on your behalf outside a guarded call. The user-facing view of a breaker stays at the level of ExternalService.available?/1, ExternalService.blown?/1, and ExternalService.reset/1 — the concept, not the individual operations.

Summary

Types

Backend-private state, produced by install/2 and passed to every other callback.

t()

An installed circuit breaker: the backend module paired with its config.

Callbacks

Reports whether the breaker will currently admit a call.

Installs the circuit breaker for service.

Records a single failure against the breaker.

Tears the breaker down. Must be safe to call more than once.

Closes the breaker and discards its recorded failures.

Types

config()

@type config() :: term()

Backend-private state, produced by install/2 and passed to every other callback.

service()

@type service() :: ExternalService.service()

t()

@type t() :: {module(), config()}

An installed circuit breaker: the backend module paired with its config.

Callbacks

ask(service, config)

@callback ask(service(), config()) :: :ok | :blown

Reports whether the breaker will currently admit a call.

A breaker that does not exist (for example because the service was stopped while a call was in flight) must be reported as :blown rather than raising.

install(service, options)

@callback install(service(), options :: keyword()) :: {:ok, config()}

Installs the circuit breaker for service.

Receives the validated :circuit_breaker options with any backend-specific options merged in, and returns the backend's config term.

melt(service, config)

@callback melt(service(), config()) :: :ok

Records a single failure against the breaker.

remove(service, config)

@callback remove(service(), config()) :: :ok

Tears the breaker down. Must be safe to call more than once.

reset(service, config)

@callback reset(service(), config()) :: :ok | {:error, :not_found}

Closes the breaker and discards its recorded failures.