上一篇博文介绍了mnesia的事务准备阶段,在其transaction上下文中执行了传入的函数,该函数为
fun() -> mnesia:write({user, me, 12345}) end
此后将继续分析。
mnesia_tm.erl
apply_fun(Fun, Args, Type) ->
Result = apply(Fun, Args),
case t_commit(Type) of
do_commit ->
{atomic, Result};
do_commit_nested ->
{nested_atomic, Result};
{do_abort, {aborted, Reason}} ->
{'EXIT', {aborted, Reason}};
{do_abort, Reason} ->
{'EXIT', {aborted, Reason}}
end.
mnesia.erl
write(Val) when is_tuple(Val), tuple_size(Val) > 2 ->
Tab = element(1, Val),
write(Tab, Val, write);
write(Tab, Val, LockKind) ->
case get(mnesia_activity_state) of
{?DEFAULT_ACCESS, Tid, Ts} ->
write(Tid, Ts, Tab, Val, LockKind);
{Mod, Tid, Ts} ->
Mod:write(Tid, Ts, Tab, Val, LockKind);
_ ->
abort(no_transaction)
end.
之前已由mnesia_tm:execute_outer在进程字典内记录了事务上下文信息,此处在mnesia:write内重新取回,若无法取得,则表明mnesia:write未处于某个上下文中,将中止写操作。
write(Tid, Ts, Tab, Val, LockKind)
when is_atom(Tab), Tab /= schema, is_tuple(Val), tuple_size(Val) > 2 ->
case element(1, Tid) of
ets ->
?ets_insert(Tab, Val),
ok;
tid ->
Store = Ts#tidstore.store,
Oid = {Tab, element(2, Val)},
case LockKind of
write ->
mnesia_locker:wlock(Tid, Store, Oid); %%----------->下一章 锁请求过程
sticky_write ->
mnesia_locker:sticky_wlock(Tid, Store, Oid);
_ ->
abort({bad_type, Tab, LockKind})
end,
write_to_store(Tab, Store, Oid, Val);
Protocol ->
do_dirty_write(Protocol, Tab, Val)
end;
此处Store为事务管理器创建的临时ets表,Oid为{表名,键名},将来会频繁用到。事务写要求mnesia向所有副本结点请求锁,因此需要一次全局锁请求广播,锁请求过程将在下一章中进行分析,此处先分析请求到锁的情形。
一旦获得锁,mnesia需要进行临时数据存储过程,将数据写入临时ets表中。
write_to_store(Tab, Store, Oid, Val) ->
case ?catch_val({Tab, record_validation}) of
{RecName, Arity, Type}
when tuple_size(Val) == Arity, RecName == element(1, Val) ->
case Type of
bag ->
?ets_insert(Store, {Oid, Val, write});
_ ->
?ets_delete(Store, Oid),
?ets_insert(Store, {Oid, Val, write})
end,
ok;
{'EXIT', _} ->
abort({no_exists, Tab});
_ ->
abort({bad_type, Val})
end.
write_to_store以一种不同的方式记录了数据的更新方式,即在临时表中记录{{Tab, Key}, Val, write}这样的内容,临时完成写操作。
mnesia:write将首先检查是否处于某个访问上下文中,当发现位于transaction上下文中时,需要向所有副本结点请求锁,请求到锁之后,需要把更新内容写入临时表。
未完待续...