Mnesia:意外中止,循环交易
我有 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
实际上,事实证明问题是在事务内使用 try/catch 围绕 mnesia 操作。请参阅此处了解更多信息。
Actually, turns out the problem was using try/catch around mnesia operations within a transaction. See here for more.
玛尼西亚说得对!!
根据您的代码,函数
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/2
和sc_store:insert/1
。我将假设这些函数再次不会在其内部创建交易!我假设它们直接使用 mnesia 函数(必须在事务中执行),例如:mnesia:write/1
,mnesia:read/1
,< code>mnesia:write/3 等 否则,如果您在这些函数中创建另一个事务,这肯定会是一团混乱的 mnesia 事务!现在让我们更正代码,如下所示:这是唯一需要的更改,假设函数
sc_store:insert/1
和sc_store:replace/2
访问 mnesia 写入和读取函数直接进行,也无需创建其他交易。Mnesia is right !!
According to your code, the function
insert_keylist/1
calls the functiontransactional_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 thefun 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
andsc_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:That's the only change required, assuming that the functions
sc_store:insert/1
andsc_store:replace/2
access mnesia write and read functions directly without creating other transactions also.