lib/maxwell/middleware/retry.ex

defmodule Maxwell.Middleware.Retry do
  @moduledoc """
  Retries requests if a connection is refused up to a pre-defined limit.

  Example:
      defmodule MyClient do
        use Maxwell.Builder

        middleware Maxwell.Middleware.Retry, delay: 1_000, max_retries: 3
      end

  Options:

      - delay:        number of milliseconds to wait between tries (defaults to 1_000)
      - max_retries:  maximum number of retries (defaults to 5)
  """
  use Maxwell.Middleware

  @defaults [delay: 1_000, max_retries: 5]

  def init(opts) do
    Keyword.merge(@defaults, opts)
  end

  def call(conn, next, opts) do
    retry_delay = Keyword.get(opts, :delay)
    max_retries = Keyword.get(opts, :max_retries)
    retry(conn, next, retry_delay, max_retries)
  end

  defp retry(conn, next, retry_delay, max_retries) when max_retries > 0 do
    case next.(conn) do
      {:error, :econnrefused, _conn} ->
        :timer.sleep(retry_delay)
        retry(conn, next, retry_delay, max_retries - 1)

      {:error, _reason, _conn} = err ->
        err

      conn ->
        conn
    end
  end

  defp retry(conn, next, _retry_delay, _max_retries) do
    next.(conn)
  end
end