ETS, похоже, не хранит мою вставку - PullRequest
0 голосов
/ 28 августа 2018

Я пытаюсь реализовать процесс, в котором я могу запрашивать / обновлять некоторую информацию о состоянии (я работаю над службой SMS и хочу сохранить некоторые локальные данные на основе ответов - позже я буду использовать БД, но сейчас Я хочу использовать ETS, это мой первый проект на Erlang, поэтому я думаю, что это полезно для изучения). К сожалению, кажется, что мои вставки не проходят, и я не понимаю, почему. Это модуль:

-module(st).
-compile(export_all).

maintain_state() ->
    Tab = ets:new(state, [set]),
    receive
        {Pid, lookup, Key} ->
            Pid ! ets:lookup(Tab, Key),
            maintain_state();
        {Pid, update, Key, Handler} ->
            NewState = Handler(ets:lookup(Tab, Key)),
            Status = ets:insert(Tab, NewState),
            Pid ! {Status, NewState},
            maintain_state();
        {Pid, statelist} ->
            Pid ! ets:tab2list(Tab),
            maintain_state();
        kill ->
            void
    end,
    ets:delete(Tab).

start_state_maintainer() ->
    Pid = spawn(st, maintain_state, []),
    register(state, Pid).

update_state(StateHandler) ->
    state ! {self(), update, testing, StateHandler},
    receive
        After ->
            After
    after 1000 ->
            throw("Timeout in update_state")
    end.

lookup_state() ->
    state ! {self(), lookup, testing},
    receive
        Value ->
            Value
    after 1000 ->
            throw("Timeout in lookup_state")
    end.

all_state() ->
    state ! {self(), statelist},
    receive
        Value ->
            Value
    after 1000 ->
            throw("Timeout in all_state")
    end.

Который я затем загружаю в сеансе erl:

> c(st).
> st:start_state_maintainer().
> st:lookup_state().
[]
> st:update_state(fun (St) -> {testing, myval} end).
{true, {testing, myval}}
> st:all_state().
[]

Поскольку update_state показывает true Я полагал, что вставка прошла успешно, но в таблице ничего не сохраняется. Что я делаю не так?

PS: если весь этот подход ошибочен или у вас есть другие замечания по поводу моего кода, я также был бы признателен за них.

1 Ответ

0 голосов
/ 28 августа 2018

Хорошо. Давайте снова запустим ваш код.

1> c(st). % compile your code
{ok,st}

% Before doing anything. let's get count of all ETS tables using ets:all/0
2> length(ets:all()). 
16 % So the Erlang VM has 16 tables after starting it

3> st:start_state_maintainer().
true

% Let's check count of tables again:
4> length(ets:all()).          
17 % Your process has created its own table

5> st:lookup_state().
[]

% Check count of tables again
6> length(ets:all()).
18 % Why????

7> st:update_state(fun (St) -> {testing, myval} end).
{true,{testing,myval}}
8> length(ets:all()).                                
19

9> st:all_state().   
[]
10> length(ets:all()).
20

Итак, в строке 5 в функции maintain_state/0 вы создаете таблицу ETS, а в строках 9, 14 и 17 вы снова вызываете эту функцию! Поэтому после получения каждого сообщения (кроме void) вы создаете новую таблицу ETS!
Давайте посмотрим на эти таблицы:

11> P = whereis(state). % Get process id of 'state' and assign it to P
<0.66.0>
12> Foreach = 
fun(Tab) -> 
    case ets:info(Tab, owner) of 
        P -> % If owner of table is state's pid
            io:format("Table ~p with data ~p~n"
                      ,[Tab, ets:tab2list(Tab)]); 
        _ -> 
            ok
    end 
end.
#Fun<erl_eval.6.118419387>

13> lists:foreach(Foreach, ets:all()).
Table 28691 with data []
Table 24594 with data []
Table 20497 with data [{testing,myval}]
Table 16400 with data []
ok

И после уничтожения вашего процесса у нас снова должно быть 16 таблиц:

14> exit(P, kill).
true
15> length(ets:all()).                                                                                                                      
16

У вас есть два варианта. Вы можете использовать именованные таблицы следующим образом:

maintain_state() ->
    % With 'named_table' option, we can use the name of table in code:
    Tab = ets:new(state, [set, named_table]),
    maintain_state2().

maintain_state2() ->
    receive
        {Pid, lookup, Key} ->
            Pid ! ets:lookup(state, Key), % I used name of table
            maintain_state2();
...

Или используйте таблицу в качестве аргумента maintain_state2:

maintain_state() ->
    Tab = ets:new(state, [set]),
    maintain_state2(Tab).

maintain_state2(Tab) ->
    receive
        {Pid, lookup, Key} ->
            Pid ! ets:lookup(Tab, Key),
            maintain_state2(Tab);
...

Я изменил код на один из приведенных выше примеров, и вот результат:

1> st:start_state_maintainer().
true
2> st:lookup_state().
[]
3> st:update_state(fun (St) -> {testing, myval} end).
{true,{testing,myval}}
4> st:all_state().
[{testing,myval}]
5> length(ets:all()).
17

После игры с передачей сообщений Эрланга и понимания его функциональности и его концепций, я действительно предлагаю вам изучить принципы разработки OTP и поведения OTP, такие как gen_server и использовать их вместо написания ваших собственных receive ... и Pid ! ... заявлений.

...