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.
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:
ets
inheritance mechanism: Check out ets:give_away/3
and the heir
option for table initialization.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.