View Source Retry (retry v0.18.0)

Provides a convenient interface to retrying behavior. All durations are specified in milliseconds.

Examples

use Retry
import Stream

retry with: exponential_backoff |> randomize |> cap(1_000) |> expiry(10_000) do
# interact with external service
end

retry with: linear_backoff(10, 2) |> cap(1_000) |> take(10) do
# interact with external service
end

retry with: cycle([500]) |> take(10) do
# interact with external service
end

The first retry will exponentially increase the delay, fudging each delay up to 10%, until the delay reaches 1 second and then give up after 10 seconds.

The second retry will linearly increase the retry by a factor of 2 from 10ms giving up after 10 attempts.

The third example shows how we can produce a delay stream using standard Stream functionality. Any stream of integers may be used as the value of with:.

Link to this section Summary

Functions

Retry a block of code delaying between each attempt the duration specified by the next item in the with delay stream.

Retry a block of code until halt is emitted delaying between each attempt the duration specified by the next item in the with delay stream.

Wait for a block of code to be truthy delaying between each attempt the duration specified by the next item in the delay stream.

Link to this section Functions

Link to this macro

retry(opts, clauses)

View Source (macro)

Retry a block of code delaying between each attempt the duration specified by the next item in the with delay stream.

If the block returns any of the atoms specified in atoms, a retry will be attempted. Other atoms or atom-result tuples will not be retried. If atoms is not specified, it defaults to [:error].

Similary, if the block raises any of the exceptions specified in rescue_only, a retry will be attempted. Other exceptions will not be retried. If rescue_only is not specified, it defaults to [RuntimeError].

The after block evaluates only when the do block returns a valid value before timeout.

On the other hand, the else block evaluates only when the do block remains erroneous after timeout.

Example

use Retry

retry with: exponential_backoff() |> cap(1_000) |> expiry(1_000), rescue_only: [CustomError] do
  # interact with external service
after
  result -> result
else
  error -> error
end

The after and else clauses are optional. By default, a successful value is just returned. If the timeout expires, the last erroneous value is returned or the last exception is re-raised. Essentially, this:

retry with: ... do
  ...
end

Is equivalent to:

retry with: ... do
  ...
after
  result -> result
else
  e when is_exception(e) -> raise e
  e -> e
end
Link to this macro

retry_while(args, arg2)

View Source (macro)

Retry a block of code until halt is emitted delaying between each attempt the duration specified by the next item in the with delay stream.

The return value for block is expected to be {:cont, result}, return {:halt, result} to end the retry early.

An accumulator can also be specified which might be handy if subsequent retries are dependent on the previous ones.

The initial value of the accumulator is given as a keyword argument acc:. When the :acc key is given, its value is used as the initial accumulator and the do block must be changed to use -> clauses, where the left side of -> receives the accumulated value of the previous iteration and the expression on the right side must return the :cont/:halt tuple with new accumulator value as the second element.

Once :halt is returned from the block, or there are no more elements, the accumulated value is returned.

Example

retry_while with: linear_backoff(500, 1) |> take(5) do
  call_service
  |> case do
    result = %{"errors" => true} -> {:cont, result}
    result -> {:halt, result}
  end
end

Example with acc:

retry_while acc: 0, with: linear_backoff(500, 1) |> take(5) do
  acc ->
    call_service
    |> case do
      %{"errors" => true} -> {:cont, acc + 1}
      result -> {:halt, result}
    end
end
Link to this macro

wait(stream_builder, clauses)

View Source (macro)

Wait for a block of code to be truthy delaying between each attempt the duration specified by the next item in the delay stream.

The after block evaluates only when the do block returns a truthy value. On the other hand, the else block evaluates only when the do block remains falsy after timeout.Both are optional. By default, a success value will be returned as {:ok, value} and an erroneous value will be returned as {:error, value}.

Example

wait linear_backoff(500, 1) |> take(5) do
  we_there_yet?
after
  _ ->
    {:ok, "We have arrived!"}
else
  _ ->
    {:error, "We're still on our way :("}
end