defmodule APIResponseProcessor do
defp update_log(api_url, error_message, {sleep_time, interval}, line_number, attempt_count) do
log_level =
case attempt_count < 5 do
true -> :log_only
false -> :warning
end
LogBook.main(
"Error: #{inspect(error_message)} (attempt #{attempt_count}) - Retried #{inspect(api_url)} after #{sleep_time} #{interval} . . . ",
__MODULE__,
line_number,
log_level
)
{:update_log, :ok}
end
defp retry_api_url(api_url, error_message, attempt_count, connection_type)
when attempt_count <= 29 do
with {:calculate, delay} <- calculate_delay(attempt_count),
{:ok, randomized_sleep_interval} <- MiscScripts.random_sleep(delay),
{:update_log, :ok} <-
update_log(
api_url,
error_message,
{randomized_sleep_interval / 1000, "seconds"},
38,
attempt_count
) do
ApiInterface.connect_to_api(api_url, connection_type, attempt_count)
else
glitch ->
ExceptionsHandler.raise_erroneous_value_alert(glitch, __MODULE__, __ENV__.function)
end
end
defp retry_api_url(_, error_message, attempt_count, _),
do: raise("Error after #{attempt_count} failed attempts: #{inspect(error_message)}")
defp calculate_delay(attempt_count) do
delay =
case attempt_count do
1 -> 200
2 -> 500
_ -> 1500
end
{:calculate, delay}
end
def main(
{:error, %HTTPoison.Error{id: nil, reason: _} = error_message},
api_url,
attempt_count,
connection_type
),
do: retry_api_url(api_url, error_message, attempt_count + 1, connection_type)
def main(
{:ok, response = %HTTPoison.Response{status_code: status_code}},
api_url,
attempt_count,
connection_type
) do
case status_code do
code when code in [200, 301, 404] ->
{:api_response_processor, response}
code when code in [400, 429, 502, 503, 520] ->
error_message = status_code_to_error(code)
retry_api_url(api_url, error_message, attempt_count + 1, connection_type)
_ ->
{:api_response_error, "Unexpected status code: #{status_code}"}
end
end
defp status_code_to_error(error_code) when is_integer(error_code) do
case error_code do
400 -> :bad_request_error
429 -> :too_many_requests
502 -> :bad_gateway
503 -> :service_unavailable
520 -> :no_data_received
end
end
end
defmodule MiscScripts do
@spec random_sleep(integer) :: {:ok, integer}
def random_sleep(max_interval) do
random_interval = :rand.uniform(max_interval)
Process.sleep(random_interval)
{:ok, random_interval}
end
end
The api retry function uses Process.sleep() for the delay but I’d like to use Process.send_after() since this seems to be the best practice. All the information I’ve come across suggests that send_after() can only be used within a GenServer which seems like overkill. So, two questions:
- How, if possible, can Process.send_after() be used outside of a GenServer?
- Is using a GenServer for this really overkill or no?