我有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.
答案 0 :(得分:4)
实际上,事实证明问题是在事务中使用围绕mnesia操作的try / catch。有关详情,请参阅here。
答案 1 :(得分:2)
Mnesia是对的!!
根据您的代码,函数insert_keylist/1
会有趣地调用函数transactional_execute/1
。在函数内部:transactional_execute/1
,你在事务中询问mnesia,它是否为真,你执行fun F
。但是,仔细查看有趣的F,您会看到它通过迭代函数transactional_execute/1
重新调用函数insert_iter/1
。因此,这会创建一个事务循环。因此,你的问题就出现了,因为一个进程在一个事务中执行最顶层的函数:insert_keylist/1
,这个事务创建了一个永远不会被执行的事务的嵌套循环,只是,他们不断询问mnesia他们是否处于事务中并且它再次接受他们不断询问,询问和循环而不会被执行!!
在我们修改您的代码之前,我们还没有看到函数中的最新内容:sc_store:replace/2
和sc_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).
这是唯一需要进行的更改,假设函数sc_store:insert/1
和sc_store:replace/2
直接访问mnesia写入和读取函数而不创建其他事务。