GenServer超时处理程序未被调用

时间:2019-03-26 05:58:59

标签: timeout elixir gen-server

我有一个非常简单的设置:一个GenServer,一种缓存,它以超时方式生成子GenServer,它们通过send向父级发送有关其不活动状态的消息来进行处理。

子级通过测试,以确认它在指定的超时后发送{:inactive, my_id}。问题在于,只有在孩子从未收到过调用以使其状态保持数据的情况下才会发生这种情况,在这种情况下,它永远不会超时。

为什么要处理一个呼叫才能防止超时?有没有一种方法可以在不影响超时的情况下处理呼叫?

此处有完整的测试用例:https://github.com/thure/so-genserver-timeout

孩子:

defmodule GenServerTimeoutBattery.Child do
  use GenServer

  def start_link(child_id, timeout_duration, parent_pid) do
    GenServer.start_link(__MODULE__, [child_id, timeout_duration, parent_pid], [name: String.to_atom(child_id)])
  end

  def get_data(child_id) do
    GenServer.call(String.to_atom(child_id), :get_data)
  end

  @impl true
  def init([child_id, timeout_duration, parent_pid]) do
    IO.puts('Timeout of #{timeout_duration} set for')
    IO.inspect(child_id)
    {
      :ok,
      %{
        data: "potato",
        child_id: child_id,
        parent_process: parent_pid
      },
      timeout_duration
    }
  end

  @impl true
  def handle_call(:get_data, _from, state) do
    IO.puts('Get data for #{state.child_id}')
    {
      :reply,
      state.data,
      state
    }
  end

  @impl true
  def handle_info(:timeout, state) do
    # Hibernates and lets the parent decide what to do.
    IO.puts('Sending timeout for #{state.child_id}')
    if is_pid(state.parent_process), do: send(state.parent_process, {:inactive, state.child_id})

    {
      :noreply,
      state,
      :hibernate
    }
  end
end

测试:

defmodule GenServerTimeoutBattery.Tests do
  use ExUnit.Case

  alias GenServerTimeoutBattery.Child

  test "child sends inactivity signal on timeout" do
    id = UUID.uuid4(:hex)

    assert {:ok, cpid} = Child.start_link(id, 2000, self())

    # If this call to `get_data` is removed, test passes.
    assert "potato" == Child.get_data(id)

    assert_receive {:inactive, child_id}, 3000

    assert child_id == id

    assert :ok = GenServer.stop(cpid)
  end
end

1 个答案:

答案 0 :(得分:1)

结果证明,在timeout上设置init会应用一个仅在收到呼叫或强制转换之前才有意义的超时。

每个呼叫或演员表都可以设置自己的timeout如果未指定timeout,则默认为:infinity在这一点上,文档并不明确,尽管现在对我来说很有意义。