【问题标题】:GenServer timeout handler not being called未调用 GenServer 超时处理程序
【发布时间】:2019-08-16 10:55:47
【问题描述】:

我有一个非常简单的设置:一个 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

【问题讨论】:

  • 你能展示一个重现这种行为的最小测试用例吗?您是否手动测试这是否有效?我现在的怀疑是测试过程可能会在孩子超时之前退出。
  • 我在这里设置了一个最小的测试用例:github.com/thure/so-genserver-timeout 在做这个的过程中,我发现它是一个调用来获取孩子状态的一部分,它可以防止孩子超时。不过,调用很满意,所以我无法想象这将如何防止超时。

标签: timeout elixir gen-server


【解决方案1】:

结果是在init 上设置timeout 会应用一个超时,该超时仅在它收到呼叫或强制转换之前相关。

然后每个调用或转换都可以设置自己的timeout如果没有指定timeout,则默认为:infinity文档在这一点上没有明确说明,但现在对我来说很有意义。

【讨论】:

    猜你喜欢
    • 2017-03-06
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-09
    相关资源
    最近更新 更多