DynamicSupervisor - проблема со связью с работниками - PullRequest
0 голосов
/ 23 ноября 2018

У меня проблема со связью с работниками, созданными в моем dynamicSupervisor.после запуска все мои работники я пытаюсь позвонить одному по pid и генерируется одна ошибка (всегда).В начале ребенка я держу пид с ец.Затем, когда я хочу позвонить этому ребенку, я получаю pid от ets по идентификатору.Все идет нормально.Проблема заключается в следующем:

GenServer.call(
  pid,
  {:action_project, %{project_id: project_id, pid: :erlang.pid_to_list(pid)}}
)

возвращает следующую ошибку:

[error] GenServer #PID<0.606.0> terminating
** (RuntimeError) attempted to call GenServer #PID<0.606.0> but no handle_call/3 clause was provided
    (backercamp) lib/gen_server.ex:693: MyApplication.ProjectWorker.handle_call/3
    (stdlib) gen_server.erl:661: :gen_server.try_handle_call/4
    (stdlib) gen_server.erl:690: :gen_server.handle_msg/6
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.680.0>): {:action_project, %{pid: '<0.606.0>', project_id: "23"}}
State: %{pid: '<0.606.0>', project_id: "23"}
Client #PID<0.680.0> is alive

    (stdlib) gen.erl:169: :gen.do_call/4
    (elixir) lib/gen_server.ex:921: GenServer.call/3

В чем проблема с этим вызовом?

Код DynamicSupervisor:

defmodule MyApplication.Supervisor do
  use DynamicSupervisor

  alias MyApplication.Management

  def start_link(arg) do
    DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
  end

  def init(arg) do
    DynamicSupervisor.init(arg)
  end

  def start_project_worker(project_id) do
    spec = {MyApplication.ProjectWorker, %{project_id: project_id}}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

  def start_all_project_workers() do
    Enum.each(Management.list_all_projects(), fn %{id: project_id} ->
      IO.puts("Started for project Id [#{project_id}]")
      # 1s between workers
      :timer.sleep(1000)
      start_project_worker("#{project_id}")
    end)
  end

  def action_project(pid, project_id) do
    GenServer.call(
      pid,
      {:action_project, %{project_id: project_id, pid: :erlang.pid_to_list(pid)}}
    )
  end
end

Рабочий код:

defmodule MyApplication.ProjectWorker do

  use GenServer, restart: :transient

  alias MyApplication.Settings
  alias MyApplication.Management

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

  def init(state) do
    schedule_action_project(Settings.get_frequency_ms())
    state = Map.put(state, :pid, :erlang.pid_to_list(self()))
    persist_state(state)
    {:ok, state}
  end

  def handle_info(:schedule_action_project, %{project_id: _project_id, pid: _pid} = state) do
    action_by_state(state)
    {:noreply, state}
  end

  def handle_call({:action_project}, %{project_id: _project_id, pid: _pid} = state) do
    case action_by_state(state) do
      true ->
        terminate(state)

      false ->
        {:reply, state, state}
    end
  end

  defp persist_state(state) do
    IO.puts(" :: Add project_id [#{state.project_id}] and pid #{state.pid}")
    :ets.insert_new(:project_backup, {state.project_id, state.pid})
  end

  defp delete_persist_state(project_id) do
    IO.puts(" :: Delete project_id [#{project_id}]")
    :ets.delete(:project_backup, project_id)
  end

  defp schedule_action_project(time) do
    IO.puts(" :: Schedule_action_project [#{time}]")
    Process.send_after(self(), :schedule_action_project, time)
  end

  defp terminate(%{project_id: project_id, pid: _pid} = state) do
    IO.puts(
      " :: Stop processed, everything is done! project_id [#{state.project_id}] and pid #{
        state.pid
      }"
    )

    delete_persist_state(project_id)
    {:stop, :normal, state}
  end

  defp action_by_state(%{project_id: _project_id, pid: _pid} = state) do
    action_by_project(Management.get_project!(state.project_id))
  end

  defp action_by_project(%Project{} = project) do
    #do something in project
  end
end

1 Ответ

0 голосов
/ 23 ноября 2018

Проблема в обработчике.Вы перепутали все аргументы.state является внутренним состоянием GenServer, вы не проходите через него, вы получаете его в обратном вызове.Следующая подпись будет совпадать, если в вашем штате есть карта с ключами project_id и pid, и вы называете ее как GenServer.call(pid, {:action_project}).

def handle_call({:action_project}, %{project_id: _project_id, pid: _pid} = state)

. Этот обработчик следует изменить так, чтобы он соответствовал вашему вызывающему:

def handle_call(
  {:action_project, %{project_id: _project_id, pid: _pid}},
  _from,
  state
)

Также обратите внимание, что handle_call обратный вызов получает три параметра (в отличие от handle_cast, второй является источником сообщения.


Sidenote: handle_info обратный вызов также имеет неправильную подпись.

...