Erlang:在 gen_server 崩溃并重新启动后,ets 表不会保留数据

发布于 2025-01-11 15:41:19 字数 1593 浏览 1 评论 0 原文

我有一个 gen_server,它在 ets 表中存储对象的位置,如下所示。

-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 ='$1', 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 表消失了,我丢失了所有对象数据。我搜索了这个问题,到处都说在 ets 表中存储数据可以帮助使状态持续存在,但我在任何地方都找不到实现它的代码。 我还尝试在调用 gen_server:start_link 而不是 init 之前创建 ets 表,但这会阻止 gen_server 在崩溃后重新启动。我知道从概念上讲 ets 表应该能够持久保存状态,但确实需要一些帮助来理解它在代码中的工作原理。

I have a gen_server which stores positions of objects in an ets table like this

-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 ='$1', 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.

The problem is when a supervisor starts my_gen_server and the process crashes and restarts, the ets table is gone and I lose all my object data. I searched for this problem and everywhere they say that storing data in ets table can help in making the state persist but I cannot find the code to achieve it anywhere.
I also tried creating the ets table before gen_server:start_link is called instead of init, but that prevents the gen_server from restarting at all after crash. I understand that conceptually ets table should be able to persist the state but would really like some help in understanding how it works in code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

幽蝶幻影 2025-01-18 15:41:19

ets 表链接到创建它们的进程,这就是为什么如果您在 gen_server 进程中创建表,当进程终止时,该表将被销毁。
如果您希望该表持续存在,您基本上有 2 个选择:

  • 使用 ets 继承机制:查看 ets:give_away/3 和用于表初始化的 heir 选项。
  • 保持单个进程拥有不属于您的服务器之一的表。在这种情况下,表应创建为公共命名表,并且不应在表持有进程上执行任何操作。该进程应该只存在来保存表。然后,您的服务器就可以按名称访问该表。

ets tables are linked to the process that creates them, that's why if you create the table in a gen_server process when the process terminates, the table is destroyed.
If you want the table to persist, you have basically 2 options:

  • Use the ets inheritance mechanism: Check out ets:give_away/3 and the heir option for table initialization.
  • Keep a single process owning the table that is not one of your servers. In this case, the table should be created as a public and named table and no operation should be performed on the table-holding process. That process should only exist to hold the table. Then, your servers can just access the table by name.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文