如何在GenServer中获取有关异步失败的通知?

时间:2018-02-11 01:19:06

标签: elixir gen-server

在elixir GenServer中,有同步和异步方法,handle_casthandle_call。在异步情况下,如果方法失败,我该如何获得通知?

方法失败意味着在handle_call方法中,我需要定义一些逻辑来查询/写入数据库。如果数据库操作失败,我需要通知调用者有关此失败的信息。在异步方法中,我该怎么做?

1 个答案:

答案 0 :(得分:1)

所以评论鼓励你让它死去'一般都是正确的。惯用的Erlang和Elixir要求“快速失败”,并让主管重新启动任何组件崩溃。

尽管如此,有时候崩溃并不合适;通常当你知道可能会出现负面结果时。标准库中的许多API通过返回结果元组(即{:ok, result}{:error, reason}来处理此问题,并使调用代码负责崩溃或尝试其他内容。

在您的用例中,我认为您应该只使用数据从进程中调用数据库写入/查询代码,而不使用异步方法,首先修复数据库性能。如果这确实是一个长期运行的查询,并且优化数据库并不是正确的答案,那么您的下一个最佳选择是Task模块(documentation here),Elixir的一部分&#39; s标准库 - 它为异步任务执行提供了内置功能。<​​/ p>

我知道人们不回答你的问题是多么令人沮丧,所以我会回答它;但请注意,这几乎肯定不是解决原始问题的正确方法。

关键的洞察力是将调用进程的pid传递给Worker,以便以后可以发送结果消息:

defmodule CastBackExampleWorker do
  use GenServer
  # ...
  def do_operation(args) do
    caller = self()
    ref = make_ref()
    # Pass the caller's pid to the GenServer so that it can message back later
    GenServer.cast(__MODULE__, {:do_operation, caller, ref, args})
    # hand back a unique ref for this request
    ref
  end

  # ...

  def handle_cast({:do_operation, caller, ref, args}, state) do
    case execute_operation(args) do
      {:ok, result} -> send(caller, {__MODULE__, ref, {:ok, result}})
      {:error, reason} -> send(caller, {__MODULE__, ref, {:error, reason}})
    end
    {:noreply, state}
  end
end

defmodule CastBackExampleClient do
  use GenServer
  # ...
  def handle_call(:my_real_work, _from, state) do
    # ignoring the ref, but we could stick it into our state for later...
    _ref = CastBackExampleWorker.do_operation([])
    {:reply, :ok, state}
  end

  def handle_info({CastBackExampleWorker, _ref, outcome}, state) do
    # Do something with the outcome here
    {:noreply, state}  
  end
end