Elixir Retry with Backoff
Backoff is a waiting plan that grows the delay between repeated calls. Each failed attempt waits longer than the last, up to a cap.
Delays double with each attempt
With a base of 50 and no cap in reach, the first four delays grow by doubling. The list is the proof.
defmodule Backoff do
def delay(base, attempt, cap), do: min(cap, base * Integer.pow(2, attempt))
end
IO.inspect(Enum.map(0..3, fn attempt -> Backoff.delay(50, attempt, 1000) end))A cap stops delay growth
With a cap of 300, the delay stops growing once it reaches the cap. Later attempts stay at 300.
defmodule Backoff do
def delay(base, attempt, cap), do: min(cap, base * Integer.pow(2, attempt))
end
IO.inspect(Enum.map(0..4, fn attempt -> Backoff.delay(50, attempt, 300) end))A success stops the attempts at once
A plan that fails twice and then succeeds uses only the first two delays. The third attempt is the success, so no delay follows it.
defmodule Backoff do
def delay(base, attempt, cap), do: min(cap, base * Integer.pow(2, attempt))
def run([:ok | _rest], delays), do: {Enum.reverse(delays), :ok}
def run([:error], delays), do: {Enum.reverse(delays), :error}
def run([:error | rest], delays), do: run(rest, [delay(150, length(delays), 1000) | delays])
def run([], delays), do: {Enum.reverse(delays), :error}
end
{delays, result} = Backoff.run([:error, :error, :ok], [])
IO.inspect(delays)
IO.inspect(result)Example
defmodule Backoff do
def delay(base, attempt, cap), do: min(cap, base * Integer.pow(2, attempt))
def run([:ok | _rest], delays), do: {Enum.reverse(delays), :ok}
def run([:error], delays), do: {Enum.reverse(delays), :error}
def run([:error | rest], delays), do: run(rest, [delay(100, length(delays), 300) | delays])
def run([], delays), do: {Enum.reverse(delays), :error}
end
{delays, result} = Backoff.run([:error, :error, :error, :error], [])
IO.inspect(delays)
IO.inspect(result)
IO.puts(length(delays) + 1)Key points
- Attempt: one try at a call that may succeed or fail.
- Delay: the computed pause before the next attempt.
- Growth: each delay doubles the previous delay in this plan.
- Cap: the largest delay the plan ever uses.
- Success: a successful attempt stops the plan at once.
In this example, four failures use delays of 100, then 200, then 300, with the cap holding the third delay at 300. The final failure has no next attempt, so no fourth delay appears, and the attempt count is 4.
Note
this lesson prints computed delay values only and never waits for them.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A plan uses a base of 50, doubling, and a cap of 200. What is the delay after the third failure, counting the first failure as attempt 0?