I am working on a code that will ping an external API every 15 minutes, to retrieve the response and store it in the database.
def get_forecast(city) do
app_id = My.api_key()
query_params = URI.encode_query(%{"q" => city, "APPID" => app_id})
url =
"https://api.weather.org/data/2.5/forecast?" <> query_params
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200} = response} ->
{:ok, Jason.decode!(response.body)}
{:ok, %HTTPoison.Response{status_code: status_code}} ->
{:error, {:status, status_code}}
{:error, reason} ->
{:error, reason}
end
end
I am running this inside a Task, and that Task is running inside a Genserver.
In application.ex
use Application
def start(_type, _args) do
children = [
# Starts a worker by calling: FsiIntegration.Worker.start_link(arg)
# {FsiIntegration.Worker, arg}
{Task.Supervisor, name: Integration.TaskSupervisor, restart: :transient, max_restarts: 3, max_seconds: 4000}
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Integration.Supervisor]
Supervisor.start_link(children, opts)
end
In genserver file
defmodule Integration.FsiServer do
use GenServer
@timeout 15000
#Public API
def start_task do
GenServer.start_link(__MODULE__, %{ref: nil}, name: __MODULE__)
end
def execute_task(pid) do
GenServer.call(pid, {:execute, @timeout})
end
#Callbacks API
def init(state) do
{:ok, state}
end
# In this case the task is already running, so we just return :ok.
def handle_call({:execute, _task_timeout}, _from, %{ref: ref} = state) when is_reference(ref) do
{:reply, :ok, state}
end
# The task is not running yet, so let's start it.
def handle_call({:execute, task_timeout}, _from, %{ref: nil} = state) do
IO.inspect state
task =
Task.Supervisor.start_child(Integration.TaskSupervisor, fn ->**
{:ok, _} = IntegrationGet.get_forecast("city")**
end)
{:reply, :ok, %{state | ref: task.ref}}
end
# The task completed successfully
def handle_info({ref, answer}, %{ref: ref} = state) do
Process.demonitor(ref, [:flush])
{:noreply, %{state | ref: nil}}
end
# The task failed
def handle_info({:DOWN, ref, :process, _pid, _reason}, %{ref: ref} = state) do
{:noreply, %{state | ref: nil}}
end
end
My questions are :
I know I have to improve on the below code. Can you guys me insight on how to make this better
use Application
def start(_type, _args) do
children = [
# Starts a worker by calling: FsiIntegration.Worker.start_link(arg)
# {FsiIntegration.Worker, arg}
{Task.Supervisor, name: Integration.TaskSupervisor, restart: :transient, max_restarts: 3, max_seconds: 4000}
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Integration.Supervisor]
Supervisor.start_link(children, opts)
end
def handle_call({:execute, task_timeout}, _from, %{ref: nil} = state) do
IO.inspect state
task =
Task.Supervisor.start_child(Integration.TaskSupervisor, fn ->
{:ok, _} = IntegrationGet.get_forecast("city")
end)
{:reply, :ok, %{state | ref: task.ref}}
end
-
The time interval between restarts I have set max_seconds to 5 seconds. But the restart happens immediately.
-
Is Task.Supervisor.start_child is good for making an external API request. Just it will make a single API request in this case. Or I have to go for async_nolink or async under Task module
My use case is request an external API and store the response in the DB.