Erlang:ets table 在 gen_server 崩溃并重启后不保留数据
Erlang: ets table does not persist data after gen_server crashes and restarts
我有一个 gen_server 存储对象在 ets 中的位置 table 像这样
-module(my_gen_server).
-record(slot, {position, object}).
-behavior(gen_server).
%% API
-export([start_link/1, init/1, move/2, handle_call/3, handle_cast/2, get/1, delete/1]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
WidthX, HeightY = get_dims(),
ets:new(my_gen_server,[named_table, {keypos, #slot.position}]),
{ok, {WidthX, HeightY}}.
move(Object, {X,Y}) ->
gen_server:call(?MODULE, {move, Object, {X,Y}}).
handle_call({move, Object, {X,Y}}, _From, {WidthX, HeightY}) ->
case add_or_move(Object, X, Y) of
{error, Reason} ->
{reply, {error, Reason}, {WidthX, HeightY}};
_ ->
{reply, ok, {WidthX, HeightY}}
end.
search_object(Object) ->
Pos = ets:match(my_gen_server, #slot{position ='', object = Object, _='_'}),
case Pos of
[] -> {error, "Not found"};
_ -> lists:flatten(Pos)
end.
add_or_move(Object, X, Y) ->
Pos = search_object(Object),
case Pos of
{error, _Reason} ->
supervisor:start_child(my_object_sup, [Object, {X, Y}]),
ets:insert(my_gen_server, #slot{position = {X,Y}, object = Object});
_ ->
ets:insert(my_gen_server, #slot{position = {X,Y}, object = Object})
end.
问题是当主管启动 my_gen_server
并且进程崩溃并重新启动时,ets table 消失了,我丢失了所有对象数据。我搜索了这个问题,到处都说在 ets table 中存储数据有助于使状态持续存在,但我无法在任何地方找到实现它的代码。
我还尝试在调用 gen_server:start_link
之前创建 ets table 而不是调用 init,但这会阻止 gen_server 在崩溃后完全重新启动。我知道从概念上讲 ets table 应该能够保持状态,但真的希望得到一些帮助来理解它在代码中的工作方式。
ets
table 链接到创建它们的进程,这就是为什么如果您在进程终止时在 gen_server
进程中创建 table, table 被摧毁。
如果你想让 table 持续存在,你基本上有 2 个选择:
- 使用
ets
继承机制:检查 ets:give_away/3
和 heir
选项以进行 table 初始化。
- 保留一个拥有 table 的进程,该进程不是您的服务器之一。在这种情况下,table 应创建为
public
和 named table,并且不应对 table-holding 执行任何操作过程。该进程应该只存在于保存 table。然后,您的服务器就可以通过名称访问 table。
我有一个 gen_server 存储对象在 ets 中的位置 table 像这样
-module(my_gen_server).
-record(slot, {position, object}).
-behavior(gen_server).
%% API
-export([start_link/1, init/1, move/2, handle_call/3, handle_cast/2, get/1, delete/1]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
WidthX, HeightY = get_dims(),
ets:new(my_gen_server,[named_table, {keypos, #slot.position}]),
{ok, {WidthX, HeightY}}.
move(Object, {X,Y}) ->
gen_server:call(?MODULE, {move, Object, {X,Y}}).
handle_call({move, Object, {X,Y}}, _From, {WidthX, HeightY}) ->
case add_or_move(Object, X, Y) of
{error, Reason} ->
{reply, {error, Reason}, {WidthX, HeightY}};
_ ->
{reply, ok, {WidthX, HeightY}}
end.
search_object(Object) ->
Pos = ets:match(my_gen_server, #slot{position ='', object = Object, _='_'}),
case Pos of
[] -> {error, "Not found"};
_ -> lists:flatten(Pos)
end.
add_or_move(Object, X, Y) ->
Pos = search_object(Object),
case Pos of
{error, _Reason} ->
supervisor:start_child(my_object_sup, [Object, {X, Y}]),
ets:insert(my_gen_server, #slot{position = {X,Y}, object = Object});
_ ->
ets:insert(my_gen_server, #slot{position = {X,Y}, object = Object})
end.
问题是当主管启动 my_gen_server
并且进程崩溃并重新启动时,ets table 消失了,我丢失了所有对象数据。我搜索了这个问题,到处都说在 ets table 中存储数据有助于使状态持续存在,但我无法在任何地方找到实现它的代码。
我还尝试在调用 gen_server:start_link
之前创建 ets table 而不是调用 init,但这会阻止 gen_server 在崩溃后完全重新启动。我知道从概念上讲 ets table 应该能够保持状态,但真的希望得到一些帮助来理解它在代码中的工作方式。
ets
table 链接到创建它们的进程,这就是为什么如果您在进程终止时在 gen_server
进程中创建 table, table 被摧毁。
如果你想让 table 持续存在,你基本上有 2 个选择:
- 使用
ets
继承机制:检查ets:give_away/3
和heir
选项以进行 table 初始化。 - 保留一个拥有 table 的进程,该进程不是您的服务器之一。在这种情况下,table 应创建为
public
和 named table,并且不应对 table-holding 执行任何操作过程。该进程应该只存在于保存 table。然后,您的服务器就可以通过名称访问 table。