Erlang retry with capped exponential backoff
Erlang
-module(retry_backoff).
-export([with_backoff/2]).
with_backoff(Fun, Opts) ->
Max = maps:get(max_attempts, Opts, 5),
Base = maps:get(base_ms, Opts, 100),
Cap = maps:get(max_ms, Opts, 5000),
attempt(Fun, 1, Max, Base, Cap).
attempt(_Fun, N, Max, _Base, _Cap) when N > Max ->
{error, exhausted};
attempt(Fun, N, Max, Base, Cap) ->
case Fun() of
{ok, _} = Ok -> Ok;
{error, _} = Err when N =:= Max -> Err;
{error, _} ->
Delay = min(Cap, Base * (1 bsl (N - 1))),
timer:sleep(Delay + rand:uniform(max(1, Delay div 2))),
attempt(Fun, N + 1, Max, Base, Cap)
end.