Рассмотрим работу ets_manager подробнее. Я буду рассматривать только функции относящиеся к менеджеру, опуская функционал обычно присутствующий в любом модуле реализующем gen_server.
Основная функция модуля с которой общаются остальные процессы называется new и повторяет поведение ets:new:
new(Name, Options) when is_atom(Name), is_list(Options) ->
Key = {Name, Options},
Server = whereis(?MODULE),
case gen_server:call(Server, {get, Key}) of
{ok, Tag} ->
receive
{'ETS-TRANSFER', Table, Server, Tag} ->
Table
after
?TIMEOUT ->
exit(timeout)
end;
{error, not_found} ->
O = case lists:keymember(heir, 1, Options) of
true ->
Options;
false ->
[{heir, Server, Key} | Options]
end,
ets:new(Name, O)
end.
При вызове функции она сначала запрашивает у рабочего процесса ets_manager таблицу с переданными данными и если таблица найдена, то ожидается сообщение передачи таблицы функцией ets:give_away. Если у менеджера нет такой таблицы, то она создается с указанием рабочего процесса ets_manager как наследника.
На стороне рабочего процесса менеджера вызов gen_server:call обрабатывается с помощью функции-обработчика handle_call:
handle_call({get, Key}, {Process, Tag}, Tables) ->
case dict:find(Key, Tables) of
{ok, Table} ->
true = ets:give_away(Table, Process, Tag),
{reply, {ok, Tag}, dict:erase(Key, Tables)};
error ->
{reply, {error, not_found}, Tables}
end;
handle_call(_Request, _From, State) ->
{reply, {error, badarg}, State}.
Первым делом мы ищем таблицу в переменной состояния процесса - Tables, являющейся экземпляром dict. Если таблица найдена, то она передается клиенту с помощью вызова ets:give_away.
Последняя значимая функция менеджера - обработчик сообщения о наследовании таблицы handle_info:
handle_info({'ETS-TRANSFER', Table, _, Key}, Tables) ->
{noreply, dict:store(Key, Table, Tables)};
handle_info(_Info, State) ->
{noreply, State}.
При получении сообщения о наследовании таблицы (в случае завершения процесса работающего с таблицей) таблица сохраняется в состоянии рабочего процесса ets_managet - Tables.
Так как функциональность рабочего процесса ets_manager минимальна (функции handle_call и handle_info), вероятность его неожиданного завершения намного меньше чем процессов работающих с таблицами ets. Таким образом ets_manager позволяет сохранять данные ets таблиц между перезапусками процессов работающих с ets таблицами. Функция ets_manager:new повторяет поведение ets:new, что позволяет просто заменить вызов ets:new на вызов ets_manager:new. Рассмотри работу менеджера подробнее.
Первым делом запускаем рабочий процесс ets_manager:
1> ets_manager:start().
{ok,<0.34.0>}
После этого создаем таблицу с помощью функции ets_manager:new (используя такие же параметры как и при вызове ets:new) и добавляем тестовые данные:
2> T = ets_manager:new(test, [set, private]).
16400
3> ets:insert(T, [{1, one}, {2, two}]).
true
4> ets:i(T).
<1 > {2,two}
<2 > {1,one}
EOT (q)uit (p)Digits (k)ill /Regexp -->q
ok
Теперь завершаем текущий процесс оболочки, владеющий таблицей:
5> exit(). *** Shell process terminated! *** Eshell V5.8.3 (abort with ^G)
После повторного создания таблицы через ets_manager:new старые данные все еще на месте:
1> T = ets_manager:new(test, [set, private]).
16400
2> ets:i(T).
<1 > {2,two}
<2 > {1,one}
EOT (q)uit (p)Digits (k)ill /Regexp -->q
ok
Add comment