我有一个非常简单的设置:一个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
答案 0 :(得分:1)
结果证明,在timeout
上设置init
会应用一个仅在收到呼叫或强制转换之前才有意义的超时。
每个呼叫或演员表都可以设置自己的timeout
。 如果未指定timeout
,则默认为:infinity
。在这一点上,文档并不明确,尽管现在对我来说很有意义。