How do I reset my db and make call to my supervisor functions every time I run mix phx.server?

How do I reset my db and make call to my supervisor compute_blog_json_data functions automatically every time I run mix phx.server? If I run iex -S mix and manually call MyApp.supervisor compute_blog_json_data(), it does exactly what I want but how do I automate it?

This is my application/supervisor structure

lib
|
|___my_app
|     |___aggregator
|     |              |____________ post.ex, comments.ex, supervisor.ex
|     |
|     |_____application.ex
|               
|________ my_app.ex

Here is my code for /lib/aggregator/supervisor.ex

defmodule MyApp.Aggregator.Supervisor do
  alias MyApp.Aggregator

  use Supervisor

  def start_link(opts) do
    Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
  end

  def init(opts) do
    children = [
      {Task.Supervisor, name: Aggregator.TaskSupervisor}
    ]

    Supervisor.init(children, strategy: :rest_for_one)
  end
end

code for /lib/aggregator/client_1.ex

defmodule MyApp.Aggregator.Client1 do
      def compute_blog_json_data(_opts) do
          .......
          calls external api and computes json
          ......
      end
end

code for /lib/aggregator/client_2.ex

def compute_blog_json_data(_opts) do
.......
calls external api and computes json
......
end

code for /lib/my_app/application.ex

defmodule MyApp.Application do
  @moduledoc false

  use Application

  @impl true
  def start(_type, _args) do
    children = [
      MyApp.Aggregator.Supervisor
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  @impl true
  def config_change(changed, _new, removed) do
    MyApp.Endpoint.config_change(changed, removed)
    :ok
  end
end

code for lib/my_app.ex

defmodule MyApp do

    alias MyApp.Aggregator

    @clients [Aggregator.Client_1, Aggregator.Client_2]

    def compute_blog_json_data(opts \\ []) do
      timeout = opts[:timeout] || 2_000
      clients = opts[:clients] || @clients

      clients
      |> Enum.map(&async_query(&1, opts))
      |> Task.yield_many(timeout)
      |> Enum.map(fn {task, res} -> res || Task.shutdown(task, :brutal_kill) end)
      |> Enum.flat_map(fn
        {:ok, results } -> results
        _ -> []
      end)
      |> channel_store_data()
    end

    defp channel_store_data(data_list) do
     ...
     inserts inside db with insert_all
     ....
    end

    defp async_query(client, opts) do
      Task.Supervisor.async_nolink(Aggregator.TaskSupervisor,
        client, :compute_blog_json_data, [opts], shutdown: :brutal_kill
      )
    end
  end

If I understand the question, you can likely use a Statically supervised task:

defmodule MyTask do
  use Task

  def start_link(arg) do
    Task.start_link(__MODULE__, :run, [arg])
  end

  def run(arg) do
    # ...
  end
end

Add it to the supervision tree:

defmodule MyApp.Application do

  def start(_type, _args) do
    children = [
      MyApp.Aggregator.Supervisor,
      {MyTask, arg} # order matters
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end

...

end

The task will run and shutdown after completion.

https://hexdocs.pm/elixir/Task.html#module-statically-supervised-tasks

2 Likes

It will run asynchronous though – as in later children of the supervisor will start before the task finished. That might or might not be the required behaviour.

1 Like