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

How can I get notification on async failed in GenServer?

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

方法失败意味着在 handle_call 方法中,我需要定义一些逻辑以 query/write 到数据库。如果数据库操作失败,我需要将此失败通知调用者。在异步方法中,我该怎么做?

所以鼓励您 'let it die' 的评论通常是正确的。惯用的 Erlang 和 Elixir 调用 'failing fast',并让主管重启任何崩溃的组件。

也就是说,有时崩溃是不合适的;通常是在您知道可能会出现负面结果时。标准库中的许多 API 通过返回结果元组来处理此问题,即 {:ok, result}{:error, reason} 并使调用代码负责崩溃或尝试其他操作。

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

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

关键的见解是将调用进程的 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