Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85710687
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
4 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/tesla/middleware/retry.ex b/lib/tesla/middleware/retry.ex
index eeb8d81..e6fc744 100644
--- a/lib/tesla/middleware/retry.ex
+++ b/lib/tesla/middleware/retry.ex
@@ -1,60 +1,98 @@
defmodule Tesla.Middleware.Retry do
@behaviour Tesla.Middleware
@moduledoc """
- Retry the HTTP call in case of connection error by default (`nxdomain`, `connrefused` etc).
- Application error checking for retry can be customized through `:should_retry` option by
+ Retry using exponential backoff and full jitter. This middleware only retries in the
+ case of connection errors (`nxdomain`, `connrefused` etc). Application error
+ checking for retry can be customized through `:should_retry` option by
providing a function in returning a boolean.
+ ## Backoff algorithm
+
+ The backoff algorithm optimizes for tight bounds on completing a request successfully.
+ It does this by first calculating an exponential backoff factor based on the
+ number of retries that have been performed. It then multiplies this factor against the
+ base delay. The total maximum delay is found by taking the minimum of either the calculated delay
+ or the maximum delay specified. This creates an upper bound on the maximum delay
+ we can see.
+
+ In order to find the actual delay value we take a random number between 0 and
+ the maximum delay based on a uniform distribution. This randomness ensures that
+ our retried requests don't "harmonize" making it harder for the downstream
+ service to heal.
+
### Example
```
defmodule MyClient do
use Tesla
plug Tesla.Middleware.Retry,
delay: 500,
max_retries: 10,
+ max_delay: 4_000,
should_retry: fn
{:ok, %{status: status}} when status in [400, 500] -> true
{:ok, _} -> false
{:error, _} -> true
end
end
```
### Options
- - `:delay` - number of milliseconds to wait before retrying (defaults to 1000)
+ - `:delay` - The base delay in milliseconds (defaults to 50)
- `:max_retries` - maximum number of retries (defaults to 5)
+ - `:max_delay` - maximum delay in milliseconds (defaults to 5000)
- `:should_retry` - function to determine if request should be retried
"""
@defaults [
- delay: 1000,
- max_retries: 5
+ delay: 50,
+ max_retries: 5,
+ max_delay: 5_000
]
@doc false
def call(env, next, opts) do
opts = opts || []
- delay = Keyword.get(opts, :delay, @defaults[:delay])
- max_retries = Keyword.get(opts, :max_retries, @defaults[:max_retries])
- should_retry = Keyword.get(opts, :should_retry, &match?({:error, _}, &1))
- retry(env, next, delay, max_retries, should_retry)
+ context = %{
+ retries: 0,
+ delay: Keyword.get(opts, :delay, @defaults[:delay]),
+ max_retries: Keyword.get(opts, :max_retries, @defaults[:max_retries]),
+ max_delay: Keyword.get(opts, :max_delay, @defaults[:max_delay]),
+ should_retry: Keyword.get(opts, :should_retry, &match?({:error, _}, &1))
+ }
+
+ retry(env, next, context)
end
- defp retry(env, next, _delay, retries, _should_retry) when retries <= 1 do
+ # If we have max retries set to 0 don't retry
+ defp retry(env, next, %{max_retries: 0}), do: Tesla.run(env, next)
+
+ # If we're on our last retry then just run and don't handle the error
+ defp retry(env, next, %{max_retries: max, retries: max}) do
Tesla.run(env, next)
end
- defp retry(env, next, delay, retries, should_retry) do
+ # Otherwise we retry if we get a retriable error
+ defp retry(env, next, context) do
res = Tesla.run(env, next)
- if should_retry.(res) do
- :timer.sleep(delay)
- retry(env, next, delay, retries - 1, should_retry)
+ if context.should_retry.(res) do
+ backoff(context.max_delay, context.delay, context.retries)
+ context = update_in(context, [:retries], &(&1 + 1))
+ retry(env, next, context)
else
res
end
end
+
+ # Exponential backoff with jitter
+ defp backoff(cap, base, attempt) do
+ factor = :math.pow(2, attempt)
+ max_sleep = trunc(min(cap, base * factor))
+ delay = :rand.uniform(max_sleep)
+
+ :timer.sleep(delay)
+ end
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Sep 19, 1:56 AM (17 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1765238
Default Alt Text
(4 KB)
Attached To
Mode
R28 tesla
Attached
Detach File
Event Timeline
Log In to Comment