Рассмотрим работу 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