Skip to content

Commit

Permalink
Merge pull request #24 from bitwalker/retry-middleware
Browse files Browse the repository at this point in the history
Implement retry middleware
  • Loading branch information
bitwalker committed Jan 15, 2017
2 parents dc9e19d + 786ad00 commit aff9d04
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
48 changes: 48 additions & 0 deletions lib/maxwell/middleware/retry.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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)

with %Maxwell.Conn{} = conn <- request(conn, opts),
%Maxwell.Conn{} = conn <- retry(conn, next, retry_delay, max_retries),
do: response(conn, opts)
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
54 changes: 54 additions & 0 deletions test/maxwell/middleware/retry_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
defmodule RetryTest do
use ExUnit.Case, async: false
alias Maxwell.Conn

defmodule LaggyAdapter do
def start_link, do: Agent.start_link(fn -> 0 end, name: __MODULE__)

def call(%{path: path} = conn) do
conn = %{conn | state: :sent}
Agent.get_and_update __MODULE__, fn retries ->
response = case path do
"/ok" -> %{conn | status: 200, resp_body: "ok"}
"/maybe" when retries < 5 -> {:error, :econnrefused, conn}
"/maybe" -> %{conn | status: 200, resp_body: "maybe"}
"/nope" -> {:error, :econnrefused, conn}
"/boom" -> {:error, :boom, conn}
end

{response, retries + 1}
end
end
end


defmodule Client do
use Maxwell.Builder

middleware Maxwell.Middleware.Retry, delay: 10, max_retries: 10

adapter LaggyAdapter
end

setup do
{:ok, _} = LaggyAdapter.start_link
:ok
end

test "pass on successful request" do
assert Conn.put_path("/ok") |> Client.get! |> Conn.get_resp_body() == "ok"
end

test "pass after retry" do
assert Conn.put_path("/maybe") |> Client.get! |> Conn.get_resp_body() == "maybe"
end

test "raise error if max_retries is exceeded" do
assert_raise Maxwell.Error, fn -> Conn.put_path("/nope") |> Client.get! end
end

test "raise error if error other than econnrefused occurs" do
assert_raise Maxwell.Error, fn -> Conn.put_path("/boom") |> Client.get! end
end

end

0 comments on commit aff9d04

Please sign in to comment.