Elixir主管-init-动态启动孩子

时间:2016-08-31 12:55:03

标签: elixir

我有一个简单的管理员模块,如:

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

我想在开始应用程序时做的是查找数据库并为db中的每个用户条目调用create_user / 1.

我在init中尝试了Process.send_after()...但是它给出了错误(**(EXIT)进程试图调用自己)

尝试在此模块中执行此操作是否有意义?我应该设置另一个工作人员(genserver)然后查询DB并调用此Users.Supervisor.create_user / 1?例如:

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

1 个答案:

答案 0 :(得分:3)

您无法在主管的init/1回调中启动动态子项,因为此函数会将子规范返回给新启动的主管进程。换句话说,在此回调返回之前,主管不知道如何启动子项。

仅仅为了进行一些初始化工作而设置GenServer似乎太多了。更好的想法是产生一个临时过程来完成工作并立即退出。

为此,您可以使用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)

此类任务可以从应用程序的主模块中生成。但请注意,如果此任务失败,退出信号将被发送回呼叫者。