【发布时间】: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