mnesia_recover.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,169 行 · 第 1/3 页
ERL
1,169 行
false -> {noreply, State}; true -> State2 = add_remote_decision(Node, D, State), {noreply, State2} end end;handle_cast({announce_all, Nodes}, State) -> announce_all(Nodes), {noreply, State};handle_cast(Msg, State) -> error("~p got unexpected cast: ~p~n", [?MODULE, Msg]), {noreply, State}.%%----------------------------------------------------------------------%% Func: handle_info/2%% Returns: {noreply, State} |%% {noreply, State, Timeout} |%% {stop, Reason, State} (terminate/2 is called)%%----------------------------------------------------------------------%% No need for buffering%% handle_info(Msg, State) when State#state.initiated == false ->%% %% Buffer early messages%% Msgs = State#state.early_msgs,%% {noreply, State#state{early_msgs = [{info, Msg} | Msgs]}};handle_info({connect_nodes, Ns, From}, State) -> handle_call({connect_nodes,Ns},From,State);handle_info(check_overload, S) -> %% Time to check if mnesia_tm is overloaded case whereis(mnesia_tm) of Pid when pid(Pid) -> Threshold = 100, Prev = S#state.tm_queue_len, {message_queue_len, Len} = process_info(Pid, message_queue_len), if Len > Threshold, Prev > Threshold -> What = {mnesia_tm, message_queue_len, [Prev, Len]}, mnesia_lib:report_system_event({mnesia_overload, What}), {noreply, S#state{tm_queue_len = 0}}; Len > Threshold -> {noreply, S#state{tm_queue_len = Len}}; true -> {noreply, S#state{tm_queue_len = 0}} end; undefined -> {noreply, S} end;handle_info(garb_decisions, State) -> do_garb_decisions(), {noreply, State};handle_info({force_decision, Tid}, State) -> %% Enforce a transaction recovery decision, %% if we still are waiting for the outcome case State#state.unclear_decision of U when U#decision.tid == Tid -> verbose("Decided to abort transaction ~p since " "max_wait_for_decision has been exceeded~n", [Tid]), D = U#decision{outcome = aborted}, State2 = add_remote_decision(node(), D, State), {noreply, State2}; _ -> {noreply, State} end;handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor -> mnesia_lib:dbg_out("~p was ~p~n",[?MODULE, R]), {stop, shutdown, State};handle_info(Msg, State) -> error("~p got unexpected info: ~p~n", [?MODULE, Msg]), {noreply, State}.%%----------------------------------------------------------------------%% Func: terminate/2%% Purpose: Shutdown the server%% Returns: any (ignored by gen_server)%%----------------------------------------------------------------------terminate(Reason, State) -> mnesia_monitor:terminate_proc(?MODULE, Reason, State).%%----------------------------------------------------------------------%% Func: code_change/3%% Purpose: Upgrade process when its code is to be changed%% Returns: {ok, NewState}%%----------------------------------------------------------------------code_change(_OldVsn, State, _Extra) -> {ok, State}.%%%----------------------------------------------------------------------%%% Internal functions%%%----------------------------------------------------------------------handle_early_msgs(State, From) -> Res = do_handle_early_msgs(State#state.early_msgs, State#state{early_msgs = [], initiated = true}), gen_server:reply(From, ok), Res.do_handle_early_msgs([Msg | Msgs], State) -> %% The messages are in reverted order case do_handle_early_msgs(Msgs, State) of%% {stop, Reason, Reply, State2} ->%% {stop, Reason, Reply, State2}; {stop, Reason, State2} -> {stop, Reason, State2}; {noreply, State2} -> handle_early_msg(Msg, State2) end;do_handle_early_msgs([], State) -> {noreply, State}. handle_early_msg({call, Msg, From}, State) -> case handle_call(Msg, From, State) of {reply, R, S} -> gen_server:reply(From, R), {noreply, S}; Other -> Other end;handle_early_msg({cast, Msg}, State) -> handle_cast(Msg, State);handle_early_msg({info, Msg}, State) -> handle_info(Msg, State).tabs() -> Curr = val(latest_transient_decision), % Do not miss any trans even Prev = val(previous_transient_decisions), % if the tabs are switched [Curr, mnesia_decision | Prev]. % Ordered by hit probabilitydecision(Tid) -> decision(Tid, tabs()).decision(Tid, [Tab | Tabs]) -> case catch ?ets_lookup(Tab, Tid) of [D] when record(D, decision) -> D; [C] when record(C, transient_decision) -> #decision{tid = C#transient_decision.tid, outcome = C#transient_decision.outcome, disc_nodes = [], ram_nodes = [] }; [] -> decision(Tid, Tabs); {'EXIT', _} -> %% Recently switched transient decision table decision(Tid, Tabs) end;decision(_Tid, []) -> no_decision.outcome(Tid, Default) -> outcome(Tid, Default, tabs()).outcome(Tid, Default, [Tab | Tabs]) -> case catch ?ets_lookup_element(Tab, Tid, 3) of {'EXIT', _} -> outcome(Tid, Default, Tabs); Val -> Val end;outcome(_Tid, Default, []) -> Default.filter_outcome(Val) -> case Val of unclear -> unclear; aborted -> aborted; presume_abort -> aborted; committed -> committed; pre_commit -> unclear end.filter_aborted(D) when D#decision.outcome == presume_abort -> D#decision{outcome = aborted};filter_aborted(D) -> D. %% Merge old decision D with new (probably remote) decisionmerge_decisions(Node, D, NewD0) -> NewD = filter_aborted(NewD0), if D == no_decision, node() /= Node -> %% We did not know anything about this txn NewD#decision{disc_nodes = []}; D == no_decision -> NewD; record(D, decision) -> DiscNs = D#decision.disc_nodes -- ([node(), Node]), OldD = filter_aborted(D#decision{disc_nodes = DiscNs}),%% mnesia_lib:dbg_out("merge ~w: NewD = ~w~n D = ~w~n OldD = ~w~n", %% [Node, NewD, D, OldD]), if OldD#decision.outcome == unclear, NewD#decision.outcome == unclear -> D; OldD#decision.outcome == NewD#decision.outcome -> %% We have come to the same decision OldD; OldD#decision.outcome == committed, NewD#decision.outcome == aborted -> %% Interesting! We have already committed, %% but someone else has aborted. Now we %% have a nice little inconcistency. The %% other guy (or some one else) has %% enforced a recovery decision when %% max_wait_for_decision was exceeded. %% We will pretend that we have obeyed %% the forced recovery decision, but we %% will also generate an event in case the %% application wants to do something clever. Msg = {inconsistent_database, bad_decision, Node}, mnesia_lib:report_system_event(Msg), OldD#decision{outcome = aborted}; OldD#decision.outcome == aborted -> %% aborted overrrides anything OldD#decision{outcome = aborted}; NewD#decision.outcome == aborted -> %% aborted overrrides anything OldD#decision{outcome = aborted}; OldD#decision.outcome == committed, NewD#decision.outcome == unclear -> %% committed overrides unclear OldD#decision{outcome = committed}; OldD#decision.outcome == unclear, NewD#decision.outcome == committed -> %% committed overrides unclear OldD#decision{outcome = committed} end end.add_remote_decisions(Node, [D | Tail], State) when record(D, decision) -> State2 = add_remote_decision(Node, D, State), add_remote_decisions(Node, Tail, State2);add_remote_decisions(Node, [C | Tail], State) when record(C, transient_decision) -> D = #decision{tid = C#transient_decision.tid, outcome = C#transient_decision.outcome, disc_nodes = [], ram_nodes = []}, State2 = add_remote_decision(Node, D, State), add_remote_decisions(Node, Tail, State2);add_remote_decisions(Node, [{mnesia_down, _, _, _} | Tail], State) -> add_remote_decisions(Node, Tail, State);add_remote_decisions(Node, [{trans_tid, serial, Serial} | Tail], State) -> sync_trans_tid_serial(Serial), case State#state.unclear_decision of undefined -> ignored; D -> case lists:member(Node, D#decision.ram_nodes) of true -> ignore; false -> abcast([Node], {what_decision, node(), D}) end end, add_remote_decisions(Node, Tail, State);add_remote_decisions(_Node, [], State) -> State.add_remote_decision(Node, NewD, State) -> Tid = NewD#decision.tid, OldD = decision(Tid), D = merge_decisions(Node, OldD, NewD), do_log_decision(D, false, undefined), Outcome = D#decision.outcome, if OldD == no_decision -> ignore; Outcome == unclear -> ignore; true -> case lists:member(node(), NewD#decision.disc_nodes) or lists:member(node(), NewD#decision.ram_nodes) of true -> tell_im_certain([Node], D); false -> ignore end end, case State#state.unclear_decision of U when U#decision.tid == Tid -> WaitFor = State#state.unclear_waitfor -- [Node], if Outcome == unclear, WaitFor == [] -> %% Everybody are uncertain, lets abort NewOutcome = aborted, CertainD = D#decision{outcome = NewOutcome, disc_nodes = [], ram_nodes = []}, tell_im_certain(D#decision.disc_nodes, CertainD), tell_im_certain(D#decision.ram_nodes, CertainD), do_log_decision(CertainD, false, undefined), verbose("Decided to abort transaction ~p " "since everybody are uncertain ~p~n", [Tid, CertainD]), gen_server:reply(State#state.unclear_pid, {ok, NewOutcome}), State#state{unclear_pid = undefined, unclear_decision = undefined, unclear_waitfor = undefined}; Outcome /= unclear -> verbose("~p told us that transaction ~p was ~p~n", [Node, Tid, Outcome]), gen_server:reply(State#state.unclear_pid, {ok, Outcome}), State#state{unclear_pid = undefined, unclear_decision = undefined, unclear_waitfor = undefined}; Outcome == unclear -> State#state{unclear_waitfor = WaitFor} end; _ -> State end.announce_all([]) -> ok;announce_all(ToNodes) -> Tid = trans_tid_serial(), announce(ToNodes, [{trans_tid,serial,Tid}], [], false). announce(ToNodes, [Head | Tail], Acc, ForceSend) -> Acc2 = arrange(ToNodes, Head, Acc, ForceSend), announce(ToNodes, Tail, Acc2, ForceSend);announce(_ToNodes, [], Acc, _ForceSend) -> send_decisions(Acc).send_decisions([{Node, Decisions} | Tail]) -> abcast([Node], {decisions, node(), Decisions}), send_decisions(Tail);send_decisions([]) -> ok.arrange([To | ToNodes], D, Acc, ForceSend) when record(D, decision) -> NeedsAdd = (ForceSend or lists:member(To, D#decision.disc_nodes) or lists:member(To, D#decision.ram_nodes)), case NeedsAdd of true -> Acc2 = add_decision(To, D, Acc), arrange(ToNodes, D, Acc2, ForceSend); false -> arrange(ToNodes, D, Acc, ForceSend) end;arrange([To | ToNodes], {trans_tid, serial, Serial}, Acc, ForceSend) -> %% Do the lamport thing plus release the others %% from uncertainity. Acc2 = add_decision(To, {trans_tid, serial, Serial}, Acc), arrange(ToNodes, {trans_tid, serial, Serial}, Acc2, ForceSend);arrange([], _Decision, Acc, _ForceSend) -> Acc.add_decision(Node, Decision, [{Node, Decisions} | Tail]) -> [{Node, [Decision | Decisions]} | Tail];add_decision(Node, Decision, [Head | Tail]) -> [Head | add_decision(Node, Decision, Tail)];add_decision(Node, Decision, []) -> [{Node, [Decision]}].
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?