aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl')
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl1175
1 files changed, 0 insertions, 1175 deletions
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl
deleted file mode 100644
index b3e8f1c386..0000000000
--- a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl
+++ /dev/null
@@ -1,1175 +0,0 @@
-%% ``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: mnesia_recover.erl,v 1.1 2008/12/17 09:53:39 mikpe Exp $
-%%
--module(mnesia_recover).
-
--behaviour(gen_server).
-
--export([
- allow_garb/0,
- call/1,
- connect_nodes/1,
- disconnect/1,
- dump_decision_tab/0,
- get_master_node_info/0,
- get_master_node_tables/0,
- get_master_nodes/1,
- get_mnesia_downs/0,
- has_mnesia_down/1,
- incr_trans_tid_serial/0,
- init/0,
- log_decision/1,
- log_master_nodes/3,
- log_mnesia_down/1,
- log_mnesia_up/1,
- mnesia_down/1,
- note_decision/2,
- note_log_decision/2,
- outcome/2,
- start/0,
- start_garb/0,
- still_pending/1,
- sync_trans_tid_serial/1,
- wait_for_decision/2,
- what_happened/3
- ]).
-
-%% gen_server callbacks
--export([init/1,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- terminate/2,
- code_change/3
- ]).
-
-
--include("mnesia.hrl").
--import(mnesia_lib, [set/2, verbose/2, error/2, fatal/2]).
-
--record(state, {supervisor,
- unclear_pid,
- unclear_decision,
- unclear_waitfor,
- tm_queue_len = 0,
- initiated = false,
- early_msgs = []
- }).
-
-%%-define(DBG(F, A), mnesia:report_event(list_to_atom(lists:flatten(io_lib:format(F, A))))).
-%%-define(DBG(F, A), io:format("DBG: " ++ F, A)).
-
--record(transient_decision, {tid, outcome}).
-
-start() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [self()],
- [{timeout, infinity}
- %%, {debug, [trace]}
- ]).
-
-init() ->
- call(init).
-
-start_garb() ->
- Pid = whereis(mnesia_recover),
- {ok, _} = timer:send_interval(timer:minutes(2), Pid, garb_decisions),
- {ok, _} = timer:send_interval(timer:seconds(10), Pid, check_overload).
-
-allow_garb() ->
- cast(allow_garb).
-
-
-%% The transaction log has either been swiched (latest -> previous) or
-%% there is nothing to be dumped. This means that the previous
-%% transaction log only may contain commit records which refers to
-%% transactions noted in the last two of the 'Prev' tables. All other
-%% tables may now be garbed by 'garb_decisions' (after 2 minutes).
-%% Max 10 tables are kept.
-do_allow_garb() ->
- %% The order of the following stuff is important!
- Curr = val(latest_transient_decision),
- Old = val(previous_transient_decisions),
- Next = create_transient_decision(),
- {Prev, ReallyOld} = sublist([Curr | Old], 10, []),
- [?ets_delete_table(Tab) || Tab <- ReallyOld],
- set(previous_transient_decisions, Prev),
- set(latest_transient_decision, Next).
-
-sublist([H|R], N, Acc) when N > 0 ->
- sublist(R, N-1, [H| Acc]);
-sublist(List, _N, Acc) ->
- {lists:reverse(Acc), List}.
-
-do_garb_decisions() ->
- case val(previous_transient_decisions) of
- [First, Second | Rest] ->
- set(previous_transient_decisions, [First, Second]),
- [?ets_delete_table(Tab) || Tab <- Rest];
- _ ->
- ignore
- end.
-
-connect_nodes([]) ->
- [];
-connect_nodes(Ns) ->
- %% Determine which nodes we should try to connect
- AlreadyConnected = val(recover_nodes),
- {_, Nodes} = mnesia_lib:search_delete(node(), Ns),
- Check = Nodes -- AlreadyConnected,
- GoodNodes = mnesia_monitor:negotiate_protocol(Check),
- if
- GoodNodes == [] ->
- %% No good noodes to connect to
- ignore;
- true ->
- %% 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
- end,
- {GoodNodes, AlreadyConnected}.
-
-disconnect(Node) ->
- mnesia_monitor:disconnect(Node),
- mnesia_lib:del(recover_nodes, Node).
-
-val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
- Value -> Value
- end.
-
-call(Msg) ->
- Pid = whereis(?MODULE),
- case Pid of
- undefined ->
- {error, {node_not_running, node()}};
- Pid ->
- link(Pid),
- Res = gen_server:call(Pid, Msg, infinity),
- unlink(Pid),
-
- %% We get an exit signal if server dies
- receive
- {'EXIT', Pid, _Reason} ->
- {error, {node_not_running, node()}}
- after 0 ->
- ignore
- end,
- Res
- end.
-
-multicall(Nodes, Msg) ->
- rpc:multicall(Nodes, ?MODULE, call, [Msg]).
-
-cast(Msg) ->
- case whereis(?MODULE) of
- undefined -> ignore;
- Pid -> gen_server:cast(Pid, Msg)
- end.
-
-abcast(Nodes, Msg) ->
- gen_server:abcast(Nodes, ?MODULE, Msg).
-
-note_decision(Tid, Outcome) ->
- Tab = val(latest_transient_decision),
- ?ets_insert(Tab, #transient_decision{tid = Tid, outcome = Outcome}).
-
-note_up(Node, _Date, _Time) ->
- ?ets_delete(mnesia_decision, Node).
-
-note_down(Node, Date, Time) ->
- ?ets_insert(mnesia_decision, {mnesia_down, Node, Date, Time}).
-
-note_master_nodes(Tab, []) ->
- ?ets_delete(mnesia_decision, Tab);
-note_master_nodes(Tab, Nodes) when list(Nodes) ->
- Master = {master_nodes, Tab, Nodes},
- ?ets_insert(mnesia_decision, Master).
-
-note_outcome(D) when D#decision.disc_nodes == [] ->
-%% ?DBG("~w: note_tmp_decision: ~w~n", [node(), D]),
- note_decision(D#decision.tid, filter_outcome(D#decision.outcome)),
- ?ets_delete(mnesia_decision, D#decision.tid);
-note_outcome(D) when D#decision.disc_nodes /= [] ->
-%% ?DBG("~w: note_decision: ~w~n", [node(), D]),
- ?ets_insert(mnesia_decision, D).
-
-log_decision(D) when D#decision.outcome /= unclear ->
- OldD = decision(D#decision.tid),
- MergedD = merge_decisions(node(), OldD, D),
- do_log_decision(MergedD, true);
-log_decision(D) ->
- do_log_decision(D, false).
-
-do_log_decision(D, DoTell) ->
- RamNs = D#decision.ram_nodes,
- DiscNs = D#decision.disc_nodes -- [node()],
- Outcome = D#decision.outcome,
- D2 =
- case Outcome of
- aborted -> D#decision{disc_nodes = DiscNs};
- committed -> D#decision{disc_nodes = DiscNs};
- _ -> D
- end,
- note_outcome(D2),
- case mnesia_monitor:use_dir() of
- true ->
- mnesia_log:append(latest_log, D2),
- if
- DoTell == true, Outcome /= unclear ->
- tell_im_certain(DiscNs, D2),
- tell_im_certain(RamNs, D2);
- true ->
- ignore
- end;
- false ->
- ignore
- end.
-
-tell_im_certain([], _D) ->
- ignore;
-tell_im_certain(Nodes, D) ->
- Msg = {im_certain, node(), D},
-%% ?DBG("~w: ~w: tell: ~w~n", [node(), Msg, Nodes]),
- abcast(Nodes, Msg).
-
-log_mnesia_up(Node) ->
- call({log_mnesia_up, Node}).
-
-log_mnesia_down(Node) ->
- call({log_mnesia_down, Node}).
-
-get_mnesia_downs() ->
- Tab = mnesia_decision,
- Pat = {mnesia_down, '_', '_', '_'},
- Downs = ?ets_match_object(Tab, Pat),
- [Node || {mnesia_down, Node, _Date, _Time} <- Downs].
-
-%% Check if we have got a mnesia_down from Node
-has_mnesia_down(Node) ->
- case ?ets_lookup(mnesia_decision, Node) of
- [{mnesia_down, Node, _Date, _Time}] ->
- true;
- [] ->
- false
- end.
-
-mnesia_down(Node) ->
- case ?catch_val(recover_nodes) of
- {'EXIT', _} ->
- %% Not started yet
- ignore;
- _ ->
- mnesia_lib:del(recover_nodes, Node),
- cast({mnesia_down, Node})
- end.
-
-log_master_nodes(Args, UseDir, IsRunning) ->
- if
- IsRunning == yes ->
- log_master_nodes2(Args, UseDir, IsRunning, ok);
- UseDir == false ->
- ok;
- true ->
- Name = latest_log,
- Fname = mnesia_log:latest_log_file(),
- Exists = mnesia_lib:exists(Fname),
- Repair = mnesia:system_info(auto_repair),
- OpenArgs = [{file, Fname}, {name, Name}, {repair, Repair}],
- case disk_log:open(OpenArgs) of
- {ok, Name} ->
- log_master_nodes2(Args, UseDir, IsRunning, ok);
- {repaired, Name, {recovered, _R}, {badbytes, _B}}
- when Exists == true ->
- log_master_nodes2(Args, UseDir, IsRunning, ok);
- {repaired, Name, {recovered, _R}, {badbytes, _B}}
- when Exists == false ->
- mnesia_log:write_trans_log_header(),
- log_master_nodes2(Args, UseDir, IsRunning, ok);
- {error, Reason} ->
- {error, Reason}
- end
- end.
-
-log_master_nodes2([{Tab, Nodes} | Tail], UseDir, IsRunning, WorstRes) ->
- Res =
- case IsRunning of
- yes ->
- R = call({log_master_nodes, Tab, Nodes, UseDir, IsRunning}),
- mnesia_controller:master_nodes_updated(Tab, Nodes),
- R;
- _ ->
- do_log_master_nodes(Tab, Nodes, UseDir, IsRunning)
- end,
- case Res of
- ok ->
- log_master_nodes2(Tail, UseDir, IsRunning, WorstRes);
- {error, Reason} ->
- log_master_nodes2(Tail, UseDir, IsRunning, {error, Reason})
- end;
-log_master_nodes2([], _UseDir, IsRunning, WorstRes) ->
- case IsRunning of
- yes ->
- WorstRes;
- _ ->
- disk_log:close(latest_log),
- WorstRes
- end.
-
-get_master_node_info() ->
- Tab = mnesia_decision,
- Pat = {master_nodes, '_', '_'},
- case catch mnesia_lib:db_match_object(ram_copies,Tab, Pat) of
- {'EXIT', _} ->
- [];
- Masters ->
- Masters
- end.
-
-get_master_node_tables() ->
- Masters = get_master_node_info(),
- [Tab || {master_nodes, Tab, _Nodes} <- Masters].
-
-get_master_nodes(Tab) ->
- case catch ?ets_lookup_element(mnesia_decision, Tab, 3) of
- {'EXIT', _} -> [];
- Nodes -> Nodes
- end.
-
-%% Determine what has happened to the transaction
-what_happened(Tid, Protocol, Nodes) ->
- Default =
- case Protocol of
- asym_trans -> aborted;
- _ -> unclear %% sym_trans and sync_sym_trans
- end,
- This = node(),
- case lists:member(This, Nodes) of
- true ->
- {ok, Outcome} = call({what_happened, Default, Tid}),
- Others = Nodes -- [This],
- case filter_outcome(Outcome) of
- unclear -> what_happened_remotely(Tid, Default, Others);
- aborted -> aborted;
- committed -> committed
- end;
- false ->
- what_happened_remotely(Tid, Default, Nodes)
- end.
-
-what_happened_remotely(Tid, Default, Nodes) ->
- {Replies, _} = multicall(Nodes, {what_happened, Default, Tid}),
- check_what_happened(Replies, 0, 0).
-
-check_what_happened([H | T], Aborts, Commits) ->
- case H of
- {ok, R} ->
- case filter_outcome(R) of
- committed ->
- check_what_happened(T, Aborts, Commits + 1);
- aborted ->
- check_what_happened(T, Aborts + 1, Commits);
- unclear ->
- check_what_happened(T, Aborts, Commits)
- end;
- {error, _} ->
- check_what_happened(T, Aborts, Commits);
- {badrpc, _} ->
- check_what_happened(T, Aborts, Commits)
- end;
-check_what_happened([], Aborts, Commits) ->
- if
- Aborts == 0, Commits == 0 -> aborted; % None of the active nodes knows
- Aborts > 0 -> aborted; % Someody has aborted
- Aborts == 0, Commits > 0 -> committed % All has committed
- end.
-
-%% Determine what has happened to the transaction
-%% and possibly wait forever for the decision.
-wait_for_decision(presume_commit, _InitBy) ->
- %% sym_trans
- {{presume_commit, self()}, committed};
-
-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 file
-convert_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),
- OldD = decision(Tid),
- MergedD = merge_decisions(node(), OldD, NewD),
- note_outcome(MergedD);
-
-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({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),
- {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
- false ->
- {noreply, State};
- true ->
- State2 = add_remote_decision(Node, D, State),
- {noreply, State2}
- end
- end;
-
-handle_cast({announce_all, Nodes}, State) ->
- announce_all(Nodes, tabs()),
- {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(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 probability
-
-decision(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) decision
-merge_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),
- 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),
- 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([], _Tabs) ->
- ok;
-announce_all(ToNodes, [Tab | Tabs]) ->
- case catch mnesia_lib:db_match_object(ram_copies, Tab, '_') of
- {'EXIT', _} ->
- %% Oops, we are in the middle of a 'garb_decisions'
- announce_all(ToNodes, Tabs);
- List ->
- announce(ToNodes, List, [], false),
- announce_all(ToNodes, Tabs)
- end;
-announce_all(_ToNodes, []) ->
- ok.
-
-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], C, Acc, ForceSend) when record(C, transient_decision) ->
- Acc2 = add_decision(To, C, Acc),
- arrange(ToNodes, C, Acc2, ForceSend);
-
-arrange([_To | _ToNodes], {mnesia_down, _Node, _Date, _Time}, Acc, _ForceSend) ->
- %% The others have their own info about this
- Acc;
-
-arrange([_To | _ToNodes], {master_nodes, _Tab, _Nodes}, Acc, _ForceSend) ->
- %% The others have their own info about this
- Acc;
-
-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]}].
-