Nema Ga
Nema Ga

Reputation: 2600

Elixir supervisor -init- dynamically start children

I have a simple supervisor module like:

defmodule Final.Users.Supervisor do
  use Supervisor

  def start_link, do: Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)

  def init(:ok) do    
    children = [
      worker(Final.UserServer, [], restart: :temporary)
    ]

    supervise(children, strategy: :simple_one_for_one)
  end

  def create_user(id), do: Supervisor.start_child(__MODULE__, [id])

end

What I would like to do on start of application is, lookup database and call create_user/1 for every user entry in db.

I tried Process.send_after()... in init but it gives error(** (EXIT) process attempted to call itself)

Does it even make sense to try to do this in this module? Should I setup another worker(genserver) that will then query DB and call this Users.Supervisor.create_user/1 ? Example:

defmodule Final.Users.Starter do
  alias Final.Repo
  alias Final.Users.Supervisor, as: Sup
  import Ecto.Query, only: [from: 2]
  use GenServer

  def start_link() do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def init(:ok) do
    Process.send_after(self(), :started, 0)
    {:ok, %{}}
  end

  def handle_info(:started, %{}) do
    query = from(p in "users",
            select: p.id)
    ids = Final.Repo.all(query)
    Enum.each(ids, fn(x) -> Sup.create_user(x) end)

    {:noreply, %{}}
  end
end

Upvotes: 0

Views: 2109

Answers (1)

mentels
mentels

Reputation: 196

You cannot start dynamic children in the supervisor's init/1 callback as this function returns the children specification to the newly started supervisor process. In other words, the supervisor don't know how to start children before this callback returns.

Setting up a GenServer solely for doing some initialisation work seems to be too much. A better idea would be to spawn a temporary process to do the job and the quit immediately.

For this purpose you could use a Task:

Task.start_link(fn -> 
                  query = from(p in "users", select: p.id)
                  ids = Final.Repo.all(query)
                  Enum.each(ids, fn(x) -> Sup.create_user(x) end)
                end)

Such task could be spawn from your application's main module. Note however, that if this task fails an exit signal will be sent back to the caller.

Upvotes: 4

Related Questions