Sending an HTTP request each second using recurring work with genserver

I am just looking for a minimal example

Probably some minimal example with DynamicSupervisor might look like

defmodule Recorder do
  use GenServer

  def start_link(args) do
    id = Map.get(args, :id)
    GenServer.start_link(__MODULE__, args, name: id)
  end

  def init(state) do
    schedule_fetch_call()
    {:ok, state}
  end

  def handle_info(:jpeg_fetch, state) do
    # start worker under dynamic supervisor
    DynamicSupervisor.start_child(
      Recorder.ImageProcessorSupervisor,
      {Recorder.ImageProcessorWorker, state.url}
    )

    schedule_fetch_call()
    {:noreply, Map.put(state, :run, true)}
  end

  defp schedule_fetch_call do
    Process.send_after(self(), :jpeg_fetch, 1000)
  end
end

defmodule Recorder.ImageProcessorWorker do
  # We use restart: :transient because we don't want 
  # GenServer to be restarted after normal stop
  # see more https://hexdocs.pm/elixir/Supervisor.html#module-restart-values-restart
  use GenServer, restart: :transient

  def start_link(url) do
    GenServer.start_link(__MODULE__, url)
  end

  def init(url) do
    send(self(), :fetch_and_process)
    {:ok, url}
  end

  def handle_info(:fetch_and_process, url) do
    # the body of your spawn
    IO.inspect("I am being called")
    IO.inspect(DateTime.utc_now())
    result = Fincher.request(:get, url) |> IO.inspect()

    case result do
      {:error, %Mint.TransportError{reason: :closed}} ->
        # retry
        send(self(), :fetch_and_process)
        {:noreply, url}

      {:ok, response} ->
        # do something with the response...
        # and stop worker normally
        {:stop, :normal, url}
    end
  end
end

With each second it spans new Recorder.ImageProcessorWorker that immediately sends to itself :fetch_and_process (see the only handle_info callback).
That callback is either successfully finishes its work and terminates the Worker by responding {:stop, :normal} or retries. (see possible return values)

And in application.ex don’t forget to register the DynamicSupervisor

children = 
  [
    ...
    {DynamicSupervisor, strategy: :one_for_one, name: Recorder.ImageProcessorSupervisor}
  ] ++ get_children() 
1 Like