【问题标题】:Mnesia: unexpectedly getting aborted, cyclic transactionsMnesia:意外中止,循环交易
【发布时间】:2011-12-24 15:13:09
【问题描述】:

我有 5 个进程在 mnesia 表中插入/更新相同的 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、insert和lookup如下:

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.

【问题讨论】:

    标签: erlang mnesia


    【解决方案1】:

    实际上,问题在于在一个事务中使用 try/catch 来处理 mnesia 操作。请参阅here 了解更多信息。

    【讨论】:

      【解决方案2】:

      Mnesia 是对的!
      根据您的代码,函数insert_keylist/1 调用函数transactional_execute/1 很有趣。在函数内部:transactional_execute/1,你询问 mnesia 它是否已经在一个事务中,如果它是真的,你执行fun F。然而,仔细观察 fun F,您会发现它通过迭代函数:insert_iter/1 再次调用了函数:transactional_execute/1。因此,这会创建一个交易循环。因此,您的问题出现了,因为一个进程正在执行最顶层的功能:insert_keylist/1 在一个事务中创建了一个永远不会执行的事务的嵌套循环,只是,他们不断询问 mnesia 他们是否在一个事务中并且它接受了他们再一次不断地询问,询问和循环而没有被执行!

      在我们修改您的代码之前,我们还没有看到函数中的内容:sc_store:replace/2sc_store:insert/1。我将假设这些函数再次不在其内部创建事务!我将假设他们直接使用 mnesia 函数(必须在事务中执行),例如:mnesia:write/1mnesia:read/1mnesia:write/3 等。否则,如果您在这些函数中创建另一个事务,那么这肯定会是一堆 mnesia 事务!现在让我们更正如下所示的代码:

      %% 这个函数将始终确保在一个 %% mnesia 事务。所以不需要打电话 %% 事务执行 insert_iter([])-> 好的; insert_iter([{Key, Value} | KVs]) -> 案例 sc_store:lookup(Key) of {ok, _Value} -> sc_store:replace(Key, Value); {error, not_found} -> sc_store:insert(Key,Value) 结尾, 插入_iter(Kvs)。

      这是唯一需要的更改,假设函数 sc_store:insert/1sc_store:replace/2 直接访问 mnesia 写入和读取函数而不创建其他事务。

      【讨论】:

      • 所以,感谢您的帮助,但我认为这不正确。 (我试过你的建议,但没有奏效)。 Transactional_execute/1 不应该创建嵌套事务——它是专门为阻止它而设计的——当我跟踪代码时,它似乎只调用了一次 mnesia:sync_transaction。另外,当我自己重复运行多个插入过程(上面的代码)时,我没有问题,只有当我将它与多个读取过程结合起来时(它们运行几乎相同的代码,除了它们只返回 lookup/1 的值而不是替换/插入值。还有其他想法吗?
      • 实际上,我可以通过自己运行插入过程来破坏它。我只是重构了保证 transactional_execute/1 只被调用一次的代码(并通过调试会话验证它)并且它仍然中断
      • HOLY CRAP! 我想这可能是我的尝试......围绕 sc:store 函数捕获。当我删除它们时,一切正常......可能是我通过在 try/catch 中包围这些函数来拦截 mnesia 的重试/死锁避免?
      • 我希望它现在很好。在某些地方,它一定是一个小的逻辑问题。实际上,我总是尽量避免使用以下功能:mnesia:wread,我可以在您的 sc_store:replace/2 中看到。这些功能搞乱了逻辑,通常会导致副作用。这些功能本身是可以的,但它们的使用通常是错误的。尝试更改该部分。此外,对于实时访问,reading processes 可以通过mnesia events 获取即时写入值。这样他们就不会阻止writing processes。我已经用这个方法来跟踪一些参数的实时变化
      猜你喜欢
      • 2015-07-11
      • 2018-09-07
      • 1970-01-01
      • 2015-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      相关资源
      最近更新 更多