Мнезия: неожиданно прерывается, циклические транзакции - PullRequest
3 голосов
/ 08 ноября 2011

У меня есть 5 процессов, которые вставляют / обновляют те же 3 записи в таблице мнезий. Каждый из этих процессов выполняет вставку / обновление в рамках одной транзакции.

У меня есть 5 других процессов, которые читают эти самые 3 записи, также в рамках одной транзакции.

Если я не блокирую всю таблицу как часть транзакции с несколькими записями, я получаю ошибку {aborted, {cyclic, node ....}}. Моя интуиция заключается в том, что мой сценарий использования является обычным и сам по себе не должен приводить к прерыванию транзакции. Может ли кто-нибудь помочь мне с моим костяным мышлением? Все, что я делаю - это вставляю (или читаю) несколько строк в кеш (таблица mnesia) в одну транзакцию.

Вставка 3 записей выглядит следующим образом

insert_keylist(Keys) ->  
    F = fun() -> insert_iter(Keys) end,  
    transactional_execute(F).

insert_iter([]) ->
    ok;
insert_iter([{Key, Value} | KVs]) ->
   insert(Key, Value),
   insert_iter(Kvs).

insert(Key, Value) ->
  F = 
      fun() ->
        case sc_store:lookup(Key) of
            {ok, _Value}       -> sc_store:replace(Key, Value);
            {error, not_found} -> sc_store:insert(Key,Value)
        end
      end,
  transactional_execute(F).    


transactional_execute(F) ->
    case mnesia:is_transaction() of
       true -> F();
       false ->
           try mnesia:sync_transaction(F) of
               {atomic, Result}   -> Result;
               {aborted, Reason}  -> {aborted, Reason}
           catch
                ErrorCl:Error     -> {error, {ErrorCl, Error}}
        end
     end.

sc_store: замена, вставка и поиск выполняются следующим образом:

replace(Key, Value) ->
    try
       case mnesia:wread({key_to_value, Key}) of
          [#key_to_value{} = Rec] -> 
            mnesia:write(Rec#key_to_value{value = Value});
          []                       -> 
        {error, not_found}
        end
    catch
       _Err:Reason ->
         {error, Reason}
    end.

insert(Key, Value, Type, Scope, TTL, TTLMessage, Ref) ->
   try
      NowDT = calendar:now_to_datetime(erlang:now()),
      ok = mnesia:write(#key_to_value{key = Key, 
                type = Type,
                scope = Scope,
                value = Value,
                create_time_dt = NowDT,
                ttl_secs = TTL,
                ttl_message = TTLMessage,
                ref = Ref})
   catch
      _Error:Reason ->
        {error, Reason}
   end.

lookup(Key) ->
   try 
      case mnesia:read(key_to_value, Key) of
         [#key_to_value{type = Type, scope = Scope, value = Value}] -> 
            {ok, {Value, Type, Scope}};
         []                       -> 
            {error, not_found}
      end
   catch
      _Err:Reason -> {error, Reason}
   end.

Ответы [ 2 ]

4 голосов
/ 12 ноября 2011

На самом деле выясняется, что проблема заключалась в использовании try / catch для операций mnesia в транзакции. См. здесь для получения дополнительной информации.

2 голосов
/ 09 ноября 2011

Мнезия права !!
Согласно вашему коду, функция insert_keylist/1 вызывает функцию transactional_execute/1 с удовольствием. Внутри функции: transactional_execute/1, вы спрашиваете mnesia, находится ли она уже в транзакции, и если она истинна, вы выполняете fun F. Однако, присмотревшись к забавному F, вы видите, что он снова вызывает функцию: transactional_execute/1 через итерационную функцию: insert_iter/1. Таким образом, это создает цикл транзакций. Следовательно, ваша проблема возникает из-за того, что процесс выполняет самую верхнюю функцию: insert_keylist/1 в транзакции, которая создает вложенный цикл транзакций, которые никогда не выполняются; снова они продолжают спрашивать, спрашивать и зацикливаться, даже не будучи казненными !!

Прежде чем мы изменим ваш код, мы не увидели, что в функциях: sc_store:replace/2 и sc_store:insert/1. Я предполагаю, что эти функции снова НЕ создают транзакции внутри себя! Я предполагаю, что они напрямую используют функции mnesia (которые должны выполняться внутри транзакции), такие как: mnesia:write/1, mnesia:read/1, mnesia:write/3 e.t.c. В противном случае, если бы вы создавали еще одну транзакцию в этих функциях, это, несомненно, было бы беспорядком транзакций !! Теперь давайте исправим код, как показано ниже:

%% This function will always be sure to be in a 
%% mnesia transaction. So no need to call
%% transactional execute

insert_iter([])-> ok;
insert_iter([{Key, Value} | KVs]) ->
   case sc_store:lookup(Key) of
        {ok, _Value} -> sc_store:replace(Key, Value);
        {error, not_found} -> sc_store:insert(Key,Value)
    end,
    insert_iter(Kvs).

Это единственное требуемое изменение, при условии, что функции sc_store:insert/1 и sc_store:replace/2 обращаются к функциям записи и чтения mnesia напрямую, без создания других транзакций.

...