Current section
Files
Jump to
Current section
Files
lib/graph_api/middleware/retry.ex
defmodule GraphApi.Middleware.Retry do
@moduledoc """
Req step that handles retries for 429 (Too Many Requests) responses.
Uses the `Retry-After` header to determine wait time. Falls back to
exponential backoff. Maximum 3 retries.
"""
@max_retries 3
@default_retry_delay_ms 1_000
@doc """
Attaches the retry step to a Req request.
"""
@spec attach(Req.Request.t()) :: Req.Request.t()
def attach(%Req.Request{} = request) do
request
|> Req.Request.register_options([:microsoft_graph_retry_count])
|> Req.Request.append_response_steps(microsoft_graph_retry: &maybe_retry/1)
end
defp maybe_retry({request, %Req.Response{status: 429} = response}) do
retry_count = request.options[:microsoft_graph_retry_count] || 0
if retry_count < @max_retries do
delay = compute_delay(response, retry_count)
Process.sleep(delay)
updated_request =
Req.Request.put_option(request, :microsoft_graph_retry_count, retry_count + 1)
{updated_request, Req.Request.run!(updated_request)}
else
{request, response}
end
end
defp maybe_retry({request, response}) do
{request, response}
end
defp compute_delay(response, retry_count) do
case Req.Response.get_header(response, "retry-after") do
[value | _] ->
case Integer.parse(value) do
{seconds, _} -> seconds * 1_000
:error -> exponential_backoff(retry_count)
end
_ ->
exponential_backoff(retry_count)
end
end
defp exponential_backoff(retry_count) do
@default_retry_delay_ms * Integer.pow(2, retry_count)
end
end