david234

david234

Task Supervisor with max_restart and max_seconds

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
  1. The time interval between restarts I have set max_seconds to 5 seconds. But the restart happens immediately.

  2. 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.

Most Liked

kwando

kwando

The max_restarts and max_seconds options only states how many (max_restarts) restarts of a child in a time period of time (max_seconds) the supervisor should tolerate before crashing.

You can read about max_seconds here: Supervisor — Elixir v1.20.2.

  1. I would start with something simpler like this:
defmodule WeatherPoller do
  use GenServer

  def start_link([]) do
    GenServer.start_link(__MODULE__, [])
  end

  def init([]) do
    send(self(), :execute)
    {:ok, []}
  end

  def handle_info(:execute, state) do
    case IntegrationGet.get_forecast("city") do
      {:ok, response} ->
        store_response(response)

      {:error, error} ->
        handle_error(response)
    end

    {:noreply, schedule_next(state)}
  end

  defp schedule_next(state) do
    Process.send_after(self(), :execute, :timer.seconds(15))
    state
  end
end

LostKobrakai

LostKobrakai

sleep will block the process from handling anything else in the meantime. It’s usually better to use sent_interval to receive another message at a later time to trigger the restart, but staying responsive to other messages.

Where Next?

Popular in Discussions Top

blackode
Elixir Upgrading is so Simple in Ubuntu and It worked for me Ubuntu 16.04 git clone https://github.com/elixir-lang/elixir.git cd elixir...
New
arcanemachine
https://nitter.net/josevalim/status/1744395345872683471 https://twitter.com/josevalim/status/1744395345872683471
New
ricklove
I was just introduced to Elixir and Phoenix. I was told about the 2 million websocket test that was done 2 years ago. From my research, t...
New
marciol
Please, let me know if this kind of discussion already took place in another topic . Hi all, how do you consider if is better to build ...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
ben-pr-p
In general I’ve been sticking to this community style guide GitHub - christopheradams/elixir_style_guide: A community driven style guide ...
New
tomekowal
Hey guys! I want to create a toy project that shows a chart of temperature over time and updates every 5 seconds. I feel LiveView is per...
New
opsb
We’re considering our architecture from a viewpoint of scaling our traffic heavily over the next 6 months. Our current deployment is runn...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
slashdotdash
Phoenix Live View is now publicly available on GitHub. Here’s Chris McCord’s tweet announcing making it public.
New

Other popular topics Top

albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

We're in Beta

About us Mission Statement