mnesia_tm.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,898 行 · 第 1/5 页
ERL
1,898 行
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%-module(mnesia_tm).-export([ start/0, init/1, non_transaction/5, transaction/6, commit_participant/5, dirty/2, display_info/2, do_update_op/3, get_info/1, get_transactions/0, info/1, mnesia_down/1, prepare_checkpoint/2, prepare_checkpoint/1, % Internal prepare_snmp/3, do_snmp/2, put_activity_id/1, put_activity_id/2, block_tab/1, unblock_tab/1, fixtable/3 ]).%% sys callback functions-export([system_continue/3, system_terminate/4, system_code_change/4 ]).-include("mnesia.hrl").-import(mnesia_lib, [set/2]).-import(mnesia_lib, [fatal/2, verbose/2, dbg_out/2]).-record(state, {coordinators = gb_trees:empty(), participants = gb_trees:empty(), supervisor, blocked_tabs = [], dirty_queue = [], fixed_tabs = []}).%% Format on coordinators is [{Tid, EtsTabList} .....-record(prep, {protocol = sym_trans, %% async_dirty | sync_dirty | sym_trans | sync_sym_trans | asym_trans records = [], prev_tab = [], % initiate to a non valid table name prev_types, prev_snmp, types }).-record(participant, {tid, pid, commit, disc_nodes = [], ram_nodes = [], protocol = sym_trans}).start() -> mnesia_monitor:start_proc(?MODULE, ?MODULE, init, [self()]).init(Parent) -> register(?MODULE, self()), process_flag(trap_exit, true), %% Initialize the schema IgnoreFallback = mnesia_monitor:get_env(ignore_fallback_at_startup), mnesia_bup:tm_fallback_start(IgnoreFallback), mnesia_schema:init(IgnoreFallback), %% Handshake and initialize transaction recovery mnesia_recover:init(), Early = mnesia_monitor:init(), AllOthers = mnesia_lib:uniq(Early ++ mnesia_lib:all_nodes()) -- [node()], set(original_nodes, AllOthers), mnesia_recover:connect_nodes(AllOthers), %% Recover transactions, may wait for decision case mnesia_monitor:use_dir() of true -> P = mnesia_dumper:opt_dump_log(startup), % previous log L = mnesia_dumper:opt_dump_log(startup), % latest log Msg = "Initial dump of log during startup: ~p~n", mnesia_lib:verbose(Msg, [[P, L]]), mnesia_log:init(); false -> ignore end, mnesia_schema:purge_tmp_files(), mnesia_recover:start_garb(), ?eval_debug_fun({?MODULE, init}, [{nodes, AllOthers}]), case val(debug) of Debug when Debug /= debug, Debug /= trace -> ignore; _ -> mnesia_subscr:subscribe(whereis(mnesia_event), {table, schema}) end, proc_lib:init_ack(Parent, {ok, self()}), doit_loop(#state{supervisor = Parent}).val(Var) -> case ?catch_val(Var) of {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_); _VaLuE_ -> _VaLuE_ end.reply({From,Ref}, R) -> From ! {?MODULE, Ref, R};reply(From, R) -> From ! {?MODULE, node(), R}.reply(From, R, State) -> reply(From, R), doit_loop(State).req(R) -> case whereis(?MODULE) of undefined -> {error, {node_not_running, node()}}; Pid -> Ref = make_ref(), Pid ! {{self(), Ref}, R}, rec(Pid, Ref) end.rec() -> rec(whereis(?MODULE)).rec(Pid) when pid(Pid) -> receive {?MODULE, _, Reply} -> Reply; {'EXIT', Pid, _} -> {error, {node_not_running, node()}} end;rec(undefined) -> {error, {node_not_running, node()}}.rec(Pid, Ref) -> receive {?MODULE, Ref, Reply} -> Reply; {'EXIT', Pid, _} -> {error, {node_not_running, node()}} end. tmlink({From, Ref}) when reference(Ref) -> link(From);tmlink(From) -> link(From).tmpid({Pid, _Ref}) when pid(Pid) -> Pid;tmpid(Pid) -> Pid.%% Returns a list of participant transaction Tid'smnesia_down(Node) -> %% Syncronously call needed in order to avoid %% race with mnesia_tm's coordinator processes %% that may restart and acquire new locks. %% mnesia_monitor takes care of the sync case whereis(?MODULE) of undefined -> mnesia_monitor:mnesia_down(?MODULE, {Node, []}); Pid -> Pid ! {mnesia_down, Node} end.prepare_checkpoint(Nodes, Cp) -> rpc:multicall(Nodes, ?MODULE, prepare_checkpoint, [Cp]).prepare_checkpoint(Cp) -> req({prepare_checkpoint,Cp}).block_tab(Tab) -> req({block_tab, Tab}).unblock_tab(Tab) -> req({unblock_tab, Tab}).doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=Sup}=State) -> receive {_From, {async_dirty, Tid, Commit, Tab}} -> case lists:member(Tab, State#state.blocked_tabs) of false -> do_async_dirty(Tid, Commit, Tab), doit_loop(State); true -> Item = {async_dirty, Tid, Commit, Tab}, State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]}, doit_loop(State2) end; {From, {sync_dirty, Tid, Commit, Tab}} -> case lists:member(Tab, State#state.blocked_tabs) of false -> do_sync_dirty(From, Tid, Commit, Tab), doit_loop(State); true -> Item = {sync_dirty, From, Tid, Commit, Tab}, State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]}, doit_loop(State2) end; {From, start_outer} -> %% Create and associate ets_tab with Tid case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of {'EXIT', Reason} -> %% system limit Msg = "Cannot create an ets table for the " "local transaction store", reply(From, {error, {system_limit, Msg, Reason}}, State); Etab -> tmlink(From), C = mnesia_recover:incr_trans_tid_serial(), ?ets_insert(Etab, {nodes, node()}), Tid = #tid{pid = tmpid(From), counter = C}, A2 = gb_trees:insert(Tid,[Etab],Coordinators), S2 = State#state{coordinators = A2}, reply(From, {new_tid, Tid, Etab}, S2) end; {From, {ask_commit, Protocol, Tid, Commit, DiscNs, RamNs}} -> ?eval_debug_fun({?MODULE, doit_ask_commit}, [{tid, Tid}, {prot, Protocol}]), mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs), Pid = case Protocol of asym_trans when node(Tid#tid.pid) /= node() -> Args = [tmpid(From), Tid, Commit, DiscNs, RamNs], spawn_link(?MODULE, commit_participant, Args); _ when node(Tid#tid.pid) /= node() -> %% *_sym_trans reply(From, {vote_yes, Tid}), nopid end, P = #participant{tid = Tid, pid = Pid, commit = Commit, disc_nodes = DiscNs, ram_nodes = RamNs, protocol = Protocol}, State2 = State#state{participants = gb_trees:insert(Tid,P,Participants)}, doit_loop(State2); {Tid, do_commit} -> case gb_trees:lookup(Tid, Participants) of none -> verbose("Tried to commit a non participant transaction ~p~n",[Tid]), doit_loop(State); {value, P} -> ?eval_debug_fun({?MODULE,do_commit,pre},[{tid,Tid},{participant,P}]), case P#participant.pid of nopid -> Commit = P#participant.commit, Member = lists:member(node(), P#participant.disc_nodes), if Member == false -> ignore; P#participant.protocol == sym_trans -> mnesia_log:log(Commit); P#participant.protocol == sync_sym_trans -> mnesia_log:slog(Commit) end, mnesia_recover:note_decision(Tid, committed), do_commit(Tid, Commit), if P#participant.protocol == sync_sym_trans -> Tid#tid.pid ! {?MODULE, node(), {committed, Tid}}; true -> ignore end, mnesia_locker:release_tid(Tid), transaction_terminated(Tid), ?eval_debug_fun({?MODULE,do_commit,post},[{tid,Tid},{pid,nopid}]), doit_loop(State#state{participants= gb_trees:delete(Tid,Participants)}); Pid when pid(Pid) -> Pid ! {Tid, committed}, ?eval_debug_fun({?MODULE, do_commit, post}, [{tid, Tid}, {pid, Pid}]), doit_loop(State) end end; {Tid, simple_commit} -> mnesia_recover:note_decision(Tid, committed), mnesia_locker:release_tid(Tid), transaction_terminated(Tid), doit_loop(State); {Tid, {do_abort, Reason}} -> ?eval_debug_fun({?MODULE, do_abort, pre}, [{tid, Tid}]), case gb_trees:lookup(Tid, Participants) of none -> verbose("Tried to abort a non participant transaction ~p: ~p~n", [Tid, Reason]), mnesia_locker:release_tid(Tid), doit_loop(State); {value, P} -> case P#participant.pid of nopid -> Commit = P#participant.commit, mnesia_recover:note_decision(Tid, aborted), do_abort(Tid, Commit), if P#participant.protocol == sync_sym_trans -> Tid#tid.pid ! {?MODULE, node(), {aborted, Tid}}; true -> ignore end, transaction_terminated(Tid), mnesia_locker:release_tid(Tid), ?eval_debug_fun({?MODULE, do_abort, post}, [{tid, Tid}, {pid, nopid}]), doit_loop(State#state{participants= gb_trees:delete(Tid,Participants)}); Pid when pid(Pid) -> Pid ! {Tid, {do_abort, Reason}}, ?eval_debug_fun({?MODULE, do_abort, post}, [{tid, Tid}, {pid, Pid}]), doit_loop(State) end end; {From, {add_store, Tid}} -> %% new store for nested transaction case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of {'EXIT', Reason} -> %% system limit Msg = "Cannot create an ets table for a nested " "local transaction store", reply(From, {error, {system_limit, Msg, Reason}}, State); Etab -> A2 = add_coord_store(Coordinators, Tid, Etab), reply(From, {new_store, Etab}, State#state{coordinators = A2}) end; {From, {del_store, Tid, Current, Obsolete, PropagateStore}} -> opt_propagate_store(Current, Obsolete, PropagateStore), A2 = del_coord_store(Coordinators, Tid, Current, Obsolete), reply(From, store_erased, State#state{coordinators = A2}); {'EXIT', Pid, Reason} -> handle_exit(Pid, Reason, State); {From, {restart, Tid, Store}} -> A2 = restore_stores(Coordinators, Tid, Store), clear_fixtable([Store]), ?ets_match_delete(Store, '_'), ?ets_insert(Store, {nodes, node()}), reply(From, {restarted, Tid}, State#state{coordinators = A2}); {delete_transaction, Tid} -> %% used to clear transactions which are committed %% in coordinator or participant processes case gb_trees:is_defined(Tid, Participants) of false -> case gb_trees:lookup(Tid, Coordinators) of none -> verbose("** ERROR ** Tried to delete a non transaction ~p~n", [Tid]), doit_loop(State); {value, Etabs} -> clear_fixtable(Etabs), erase_ets_tabs(Etabs), transaction_terminated(Tid), doit_loop(State#state{coordinators = gb_trees:delete(Tid,Coordinators)}) end; true -> transaction_terminated(Tid),
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?