Erlang Mnesia:意外中止、循环事务

Erlang Mnesia:意外中止、循环事务,erlang,mnesia,Erlang,Mnesia,我有5个进程,在一个mnesia表中插入/更新相同的3条记录。这些流程中的每一个都在单个事务中进行插入/更新 我还有另外5个进程,在一个事务中读取这3条记录 除非我将整个表锁定为多记录事务的一部分,否则会出现{aborted,{cyclic,node….}错误。我的直觉是,我的用例是普通的,本身不应该导致事务中止。有人能帮我解决我的脑筋急转弯吗?我所做的只是在一个事务中插入(或读取)缓存(mnesia表)中的多行 插入3条记录如下所示 insert_keylist(Keys) ->

我有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_存储:替换、插入和查找如下所示:

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.

记忆是对的
根据您的代码,函数
insert\u keylist/1
有趣地调用函数
transactional\u execute/1
。在函数内部:
transactional\u execute/1
,您询问mnesia它是否已经在事务中,如果它为真,则执行
fun F
。但是,仔细观察fun F,您会发现它通过迭代函数:
insert\u iter/1
再次调用函数:
transactional\u execute/1
。因此,这创建了一个事务循环。因此,出现问题的原因是某个进程正在执行最顶层的函数:
insert\u keylist/1
在一个事务中创建一个嵌套的事务循环,而该循环中的事务永远不会执行,他们不停地问mnesia他们是否在交易中,它再次接受了他们不停地问,不停地问,不停地循环,从来没有被执行过

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

%% 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). %%此功能将始终确保处于 %%记忆交易。所以没必要打电话 %%事务执行 插入iter([])->ok; 插入_iter([{Key,Value}|KVs])-> 案例sc_存储:查找(键) {ok,_Value}->sc_store:replace(键,值); {错误,未找到}->sc\u存储:插入(键,值) 完,, 插入_iter(Kvs)。
这是唯一需要的更改,假设函数
sc_store:insert/1
sc_store:replace/2
直接访问mnesia write和read函数,而不创建其他事务

事实上,问题在于在事务中使用try/catch-around-mnesia操作。有关详细信息,请参阅。

谢谢您的帮助,但我认为这是不对的。(我试过你的建议,但没用)。Transactional_execute/1不应创建嵌套事务—它专门用于停止嵌套事务—当我跟踪代码时,它似乎只调用了一次mnesia:sync_事务。另外,当我自己反复运行多个插入进程(上面的代码)时,我没有问题,只有当我将它与多个读取进程结合起来时,我才有问题(除了返回lookup/1的值而不是替换/插入该值之外,它们运行的代码几乎相同。还有其他想法吗?实际上,我可以通过自己运行insert进程来打破它。我只是重构了代码,确保事务性_execute/1只被调用一次(并通过调试会话验证)我想这可能是我对sc:store函数的尝试…捕获。当我删除它们时,一切都正常…可能是我通过尝试/捕获来拦截mnesia的重试/避免死锁吗?我希望现在很好。在某些地方这一定是一个小的逻辑问题。实际上,我始终尽量避免使用我在sc_store:replace/2中看到的函数:
mnesia:read
。这些函数会扰乱逻辑,通常会导致副作用。这些函数本身是可以使用的,但它们的使用通常是错误的。也可以尝试更改该部分。此外,为了实时访问,
读取过程可以通过
mnesia事件立即写入值
。通过这种方式,它们不会阻止
写入过程
。我使用这种方法跟踪某些参数的实时更改