Attempt

Implements a retry budget to support a rate-limited and retry capable function execution.

This is particularly helpful in two broad cases both of which involve transient errors

Usage

See primarily:

iex#> Attempt.execute fn -> "Hello World" end
"Hello World"
iex#> Attempt.execute fn -> IO.puts "Reraise Failure!"; div(1,0) end, tries: 3
Reraise Failure!
** (ArithmeticError) bad argument in arithmetic expression
:erlang.div(1, 0)
(attempt) lib/attempt.ex:119: Attempt.execute_function/1
(attempt) lib/attempt.ex:98: Attempt.execute/6
iex#> Attempt.execute fn -> IO.puts "Try 3 times"; :error end, tries: 3
Try 3 times
Try 3 times
Try 3 times
:error
# Create a bucket that adds a new token only every 10 seconds
iex#> {:ok, bucket} = Attempt.Bucket.Token.new :test, fill_rate: 10_000
iex#> Attempt.execute fn ->
IO.puts "Try 11 times and we'll timeout claiming a token"
:error
end, tries: 11, token_bucket: bucket
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
Try 11 times and we'll timeout claiming a token
{:error, {:timeout, {GenServer, :call, [:test, :claim_token, 5000]}}}

Topics for discussion

Attempt.execute fill_rate: 1_000, burst_size: 100 do
# execute some function
end

Todo

[ ] Enforce maximum queue depth in Attempt.Bucket.Token [ ] Implement a Leaky Bucket [ ] Wire up retry backoff strategies into Attempt.execute/2 [ ] Tests [ ] Specs [ ] Improve Documentation [ ] Implement the ! version of Attempt.execute!/2

Installation

Attempt can be installed by adding attempt to your list of dependencies in mix.exs:

def deps do
[
{:attempt, "~> 0.1.0"}
]
end

The docs can be found at https://hexdocs.pm/attempt.