Mnesia:意外中止,循环交易

发布于 2024-12-14 03:02:39 字数 2357 浏览 6 评论 0原文

我有 5 个进程在 mnesia 表中插入/更新相同的 3 条记录。每个进程都在单个事务中执行插入/更新。

我还有另外 5 个进程读取这 3 条完全相同的记录,也在单个事务中。

除非我将整个表锁定为多记录事务的一部分,否则我会收到 {aborted, {circular, 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.

I have a 5 processes that insert/update the same 3 records in a mnesia table. Each of these processes does its insert/updates within a single transaction.

I have 5 other process that read these very same 3 records, also within a single transaction.

Unless I lock the entire table as part of the multi-record transaction, I get an {aborted, {cyclic, node....}} error. My intuition is that my use-case is ordinary and should not, in of itself, result in in an aborted transaction. Can someone help me with my bone-headed thinking? All I am doing is inserting (or reading) multiple rows in a cache (mnesia table) in one transaction.

Inserting the 3 records looks like this

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 and lookup are is as follows:

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烟酒忠诚 2024-12-21 03:02:39

实际上,事实证明问题是在事务内使用 try/catch 围绕 mnesia 操作。请参阅此处了解更多信息。

Actually, turns out the problem was using try/catch around mnesia operations within a transaction. See here for more.

萌梦深 2024-12-21 03:02:39

玛尼西亚说得对!!
根据您的代码,函数 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/1, mnesia:read/1,< code>mnesia:write/3 等 否则,如果您在这些函数中创建另一个事务,这肯定会是一团混乱的 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).

这是唯一需要的更改,假设函数 sc_store:insert/1sc_store:replace/2 访问 mnesia 写入和读取函数直接进行,也无需创建其他交易。

Mnesia is right !!
According to your code, the function insert_keylist/1 calls the function transactional_execute/1 with a fun. Inside the function: transactional_execute/1, you ask mnesia wether its already in a transaction, and if its true, you execute the fun F. However, looking closely in fun F, you see that it calls the function: transactional_execute/1 all over again through the iterative function: insert_iter/1. So, this creates a loop of transactions. Hence your problem is arising because a process is executing the top most function: insert_keylist/1 in a transaction which is creating a nested loop of transactions which never get executed, only, they keep asking mnesia wether they are in a transaction and it accepts yet again they keep asking and asking and looping without ever getting executed !!

Before we modify your code, we have not seen whats in the functions: sc_store:replace/2 and sc_store:insert/1. I will assume that these functions are again NOT creating transactions within themselves!! I will assume that they are directly using mnesia functions (That must be executed within a transaction), such as : mnesia:write/1, mnesia:read/1,mnesia:write/3 e.t.c. Otherwise if you were creating another transaction in these functions, surely this would be a mess of mnesia transactions!! Now lets correct the code as shown below:

%% 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).

That's the only change required, assuming that the functions sc_store:insert/1 and sc_store:replace/2 access mnesia write and read functions directly without creating other transactions also.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文