未调用 GenServer 超时处理程序
GenServer timeout handler not being called
我有一个非常简单的设置:一个 GenServer,一种缓存,它产生 child 个带有超时的 GenServer,它们通过 send
处理 parent关于他们不活动的消息。
child 通过测试确认它在指定的超时后发送 {:inactive, my_id}
。问题是,只要 child 从未接收到获取其状态数据的调用,这种情况就会发生,在这种情况下,它永远不会超时。
为什么处理一个电话要防止超时?有没有办法在不阻塞超时的情况下处理调用?
这里是完整的测试用例:https://github.com/thure/so-genserver-timeout
Child:
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
原来在 init
上设置 timeout
会应用一个超时,该超时仅在收到呼叫或演员表之前有效。
每个调用或转换都可以设置自己的 timeout
。 如果未指定 timeout
,则默认为 :infinity
。 文档在这一点上没有明确说明,但现在对我来说很有意义。
我有一个非常简单的设置:一个 GenServer,一种缓存,它产生 child 个带有超时的 GenServer,它们通过 send
处理 parent关于他们不活动的消息。
child 通过测试确认它在指定的超时后发送 {:inactive, my_id}
。问题是,只要 child 从未接收到获取其状态数据的调用,这种情况就会发生,在这种情况下,它永远不会超时。
为什么处理一个电话要防止超时?有没有办法在不阻塞超时的情况下处理调用?
这里是完整的测试用例:https://github.com/thure/so-genserver-timeout
Child:
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
原来在 init
上设置 timeout
会应用一个超时,该超时仅在收到呼叫或演员表之前有效。
每个调用或转换都可以设置自己的 timeout
。 如果未指定 timeout
,则默认为 :infinity
。 文档在这一点上没有明确说明,但现在对我来说很有意义。