mnesia_recover.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,169 行 · 第 1/3 页
ERL
1,169 行
wait_for_decision(D, InitBy) when D#decision.outcome == presume_abort -> %% asym_trans Tid = D#decision.tid, Outcome = filter_outcome(outcome(Tid, D#decision.outcome)), if Outcome /= unclear -> {Tid, Outcome}; InitBy /= startup -> %% Wait a while for active transactions %% to end and try again timer:sleep(200), wait_for_decision(D, InitBy); InitBy == startup -> {ok, Res} = call({wait_for_decision, D}), {Tid, Res} end.still_pending([Tid | Pending]) -> case filter_outcome(outcome(Tid, unclear)) of unclear -> [Tid | still_pending(Pending)]; _ -> still_pending(Pending) end;still_pending([]) -> [].load_decision_tab() -> Cont = mnesia_log:open_decision_tab(), load_decision_tab(Cont, load_decision_tab), mnesia_log:close_decision_tab().load_decision_tab(eof, _InitBy) -> ok;load_decision_tab(Cont, InitBy) -> case mnesia_log:chunk_decision_tab(Cont) of {Cont2, Decisions} -> note_log_decisions(Decisions, InitBy), load_decision_tab(Cont2, InitBy); eof -> ok end.%% Dumps DECISION.LOG and PDECISION.LOG and removes them.%% From now on all decisions are logged in the transaction log fileconvert_old() -> HasOldStuff = mnesia_lib:exists(mnesia_log:previous_decision_log_file()) or mnesia_lib:exists(mnesia_log:decision_log_file()), case HasOldStuff of true -> mnesia_log:open_decision_log(), dump_decision_log(startup), dump_decision_log(startup), mnesia_log:close_decision_log(), Latest = mnesia_log:decision_log_file(), ok = file:delete(Latest); false -> ignore end.dump_decision_log(InitBy) -> %% Assumed to be run in transaction log dumper process Cont = mnesia_log:prepare_decision_log_dump(), perform_dump_decision_log(Cont, InitBy).perform_dump_decision_log(eof, _InitBy) -> confirm_decision_log_dump();perform_dump_decision_log(Cont, InitBy) when InitBy == startup -> case mnesia_log:chunk_decision_log(Cont) of {Cont2, Decisions} -> note_log_decisions(Decisions, InitBy), perform_dump_decision_log(Cont2, InitBy); eof -> confirm_decision_log_dump() end;perform_dump_decision_log(_Cont, _InitBy) -> confirm_decision_log_dump().confirm_decision_log_dump() -> dump_decision_tab(), mnesia_log:confirm_decision_log_dump().dump_decision_tab() -> Tab = mnesia_decision, All = mnesia_lib:db_match_object(ram_copies,Tab, '_'), mnesia_log:save_decision_tab({decision_list, All}).note_log_decisions([What | Tail], InitBy) -> note_log_decision(What, InitBy), note_log_decisions(Tail, InitBy);note_log_decisions([], _InitBy) -> ok.note_log_decision(NewD, InitBy) when NewD#decision.outcome == pre_commit -> note_log_decision(NewD#decision{outcome = unclear}, InitBy);note_log_decision(NewD, _InitBy) when record(NewD, decision) -> Tid = NewD#decision.tid, sync_trans_tid_serial(Tid), note_outcome(NewD);note_log_decision({trans_tid, serial, _Serial}, startup) -> ignore;note_log_decision({trans_tid, serial, Serial}, _InitBy) -> sync_trans_tid_serial(Serial);note_log_decision({mnesia_up, Node, Date, Time}, _InitBy) -> note_up(Node, Date, Time);note_log_decision({mnesia_down, Node, Date, Time}, _InitBy) -> note_down(Node, Date, Time);note_log_decision({master_nodes, Tab, Nodes}, _InitBy) -> note_master_nodes(Tab, Nodes);note_log_decision(H, _InitBy) when H#log_header.log_kind == decision_log -> V = mnesia_log:decision_log_version(), if H#log_header.log_version == V-> ok; H#log_header.log_version == "2.0" -> verbose("Accepting an old version format of decision log: ~p~n", [V]), ok; true -> fatal("Bad version of decision log: ~p~n", [H]) end;note_log_decision(H, _InitBy) when H#log_header.log_kind == decision_tab -> V = mnesia_log:decision_tab_version(), if V == H#log_header.log_version -> ok; true -> fatal("Bad version of decision tab: ~p~n", [H]) end;note_log_decision({decision_list, ItemList}, InitBy) -> note_log_decisions(ItemList, InitBy);note_log_decision(BadItem, InitBy) -> exit({"Bad decision log item", BadItem, InitBy}).trans_tid_serial() -> ?ets_lookup_element(mnesia_decision, serial, 3).set_trans_tid_serial(Val) -> ?ets_insert(mnesia_decision, {trans_tid, serial, Val}).incr_trans_tid_serial() -> ?ets_update_counter(mnesia_decision, serial, 1).sync_trans_tid_serial(ThatCounter) when integer(ThatCounter) -> ThisCounter = trans_tid_serial(), if ThatCounter > ThisCounter -> set_trans_tid_serial(ThatCounter + 1); true -> ignore end;sync_trans_tid_serial(Tid) -> sync_trans_tid_serial(Tid#tid.counter).%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Callback functions from gen_server%%----------------------------------------------------------------------%% Func: init/1%% Returns: {ok, State} |%% {ok, State, Timeout} |%% {stop, Reason}%%----------------------------------------------------------------------init([Parent]) -> process_flag(trap_exit, true), mnesia_lib:verbose("~p starting: ~p~n", [?MODULE, self()]), set(latest_transient_decision, create_transient_decision()), set(previous_transient_decisions, []), set(recover_nodes, []), State = #state{supervisor = Parent}, {ok, State}.create_transient_decision() -> ?ets_new_table(mnesia_transient_decision, [{keypos, 2}, set, public]).%%----------------------------------------------------------------------%% Func: handle_call/3%% Returns: {reply, Reply, State} |%% {reply, Reply, State, Timeout} |%% {noreply, State} |%% {noreply, State, Timeout} |%% {stop, Reason, Reply, State} | (terminate/2 is called)%%----------------------------------------------------------------------handle_call(init, From, State) when State#state.initiated == false -> Args = [{keypos, 2}, set, public, named_table], case mnesia_monitor:use_dir() of true -> ?ets_new_table(mnesia_decision, Args), set_trans_tid_serial(0), TabFile = mnesia_log:decision_tab_file(), case mnesia_lib:exists(TabFile) of true -> load_decision_tab(); false -> ignore end, convert_old(), mnesia_dumper:opt_dump_log(scan_decisions); false -> ?ets_new_table(mnesia_decision, Args), set_trans_tid_serial(0) end, handle_early_msgs(State, From);handle_call(Msg, From, State) when State#state.initiated == false -> %% Buffer early messages Msgs = State#state.early_msgs, {noreply, State#state{early_msgs = [{call, Msg, From} | Msgs]}};handle_call({disconnect, Node}, _From, State) -> mnesia_monitor:disconnect(Node), mnesia_lib:del(recover_nodes, Node), {reply, ok, State};handle_call({connect_nodes, Ns}, From, State) -> %% Determine which nodes we should try to connect AlreadyConnected = val(recover_nodes), {_, Nodes} = mnesia_lib:search_delete(node(), Ns), Check = Nodes -- AlreadyConnected, case mnesia_monitor:negotiate_protocol(Check) of busy -> %% monitor is disconnecting some nodes retry %% the req (to avoid deadlock). erlang:send_after(2, self(), {connect_nodes,Ns,From}), {noreply, State}; [] -> %% No good noodes to connect to! %% We can't use reply here because this function can be %% called from handle_info gen_server:reply(From, {[], AlreadyConnected}), {noreply, State}; GoodNodes -> %% Now we have agreed upon a protocol with some new nodes %% and we may use them when we recover transactions mnesia_lib:add_list(recover_nodes, GoodNodes), cast({announce_all, GoodNodes}), case get_master_nodes(schema) of [] -> Context = starting_partitioned_network, mnesia_monitor:detect_inconcistency(GoodNodes, Context); _ -> %% If master_nodes is set ignore old inconsistencies ignore end, gen_server:reply(From, {GoodNodes, AlreadyConnected}), {noreply,State} end;handle_call({what_happened, Default, Tid}, _From, State) -> sync_trans_tid_serial(Tid), Outcome = outcome(Tid, Default), {reply, {ok, Outcome}, State};handle_call({wait_for_decision, D}, From, State) -> Recov = val(recover_nodes), AliveRam = (mnesia_lib:intersect(D#decision.ram_nodes, Recov) -- [node()]), RemoteDisc = D#decision.disc_nodes -- [node()], if AliveRam == [], RemoteDisc == [] -> %% No more else to wait for and we may safely abort {reply, {ok, aborted}, State}; true -> verbose("Transaction ~p is unclear. " "Wait for disc nodes: ~w ram: ~w~n", [D#decision.tid, RemoteDisc, AliveRam]), AliveDisc = mnesia_lib:intersect(RemoteDisc, Recov), Msg = {what_decision, node(), D}, abcast(AliveRam, Msg), abcast(AliveDisc, Msg), case val(max_wait_for_decision) of infinity -> ignore; MaxWait -> ForceMsg = {force_decision, D#decision.tid}, {ok, _} = timer:send_after(MaxWait, ForceMsg) end, State2 = State#state{unclear_pid = From, unclear_decision = D, unclear_waitfor = (RemoteDisc ++ AliveRam)}, {noreply, State2} end;handle_call({log_mnesia_up, Node}, _From, State) -> do_log_mnesia_up(Node), {reply, ok, State};handle_call({log_mnesia_down, Node}, _From, State) -> do_log_mnesia_down(Node), {reply, ok, State};handle_call({log_master_nodes, Tab, Nodes, UseDir, IsRunning}, _From, State) -> do_log_master_nodes(Tab, Nodes, UseDir, IsRunning), {reply, ok, State};handle_call(Msg, _From, State) -> error("~p got unexpected call: ~p~n", [?MODULE, Msg]), {noreply, State}.do_log_mnesia_up(Node) -> Yoyo = {mnesia_up, Node, Date = date(), Time = time()}, case mnesia_monitor:use_dir() of true -> mnesia_log:append(latest_log, Yoyo), disk_log:sync(latest_log); false -> ignore end, note_up(Node, Date, Time).do_log_mnesia_down(Node) -> Yoyo = {mnesia_down, Node, Date = date(), Time = time()}, case mnesia_monitor:use_dir() of true -> mnesia_log:append(latest_log, Yoyo), disk_log:sync(latest_log); false -> ignore end, note_down(Node, Date, Time).do_log_master_nodes(Tab, Nodes, UseDir, IsRunning) -> Master = {master_nodes, Tab, Nodes}, Res = case UseDir of true -> LogRes = mnesia_log:append(latest_log, Master), disk_log:sync(latest_log), LogRes; false -> ok end, case IsRunning of yes -> note_master_nodes(Tab, Nodes); _NotRunning -> ignore end, Res.%%----------------------------------------------------------------------%% Func: handle_cast/2%% Returns: {noreply, State} |%% {noreply, State, Timeout} |%% {stop, Reason, State} (terminate/2 is called)%%----------------------------------------------------------------------handle_cast(Msg, State) when State#state.initiated == false -> %% Buffer early messages Msgs = State#state.early_msgs, {noreply, State#state{early_msgs = [{cast, Msg} | Msgs]}};handle_cast({im_certain, Node, NewD}, State) -> OldD = decision(NewD#decision.tid), MergedD = merge_decisions(Node, OldD, NewD), do_log_decision(MergedD, false, undefined), {noreply, State};handle_cast({log_decision, D}, State) -> do_log_decision(D), {noreply, State};handle_cast(allow_garb, State) -> do_allow_garb(), {noreply, State};handle_cast({decisions, Node, Decisions}, State) -> mnesia_lib:add(recover_nodes, Node), State2 = add_remote_decisions(Node, Decisions, State), {noreply, State2};handle_cast({what_decision, Node, OtherD}, State) -> Tid = OtherD#decision.tid, sync_trans_tid_serial(Tid), Decision = case decision(Tid) of no_decision -> OtherD; MyD when record(MyD, decision) -> MyD end, announce([Node], [Decision], [], true), {noreply, State};handle_cast({mnesia_down, Node}, State) -> case State#state.unclear_decision of undefined -> {noreply, State}; D -> case lists:member(Node, D#decision.ram_nodes) of
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?