mnesia_dumper.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,214 行 · 第 1/3 页

ERL
1,214
字号
%% ``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_dumper).%% The InitBy arg may be one of the following:%% scan_decisions     Initial scan for decisions%% startup	      Initial dump during startup%% schema_prepare     Dump initiated during schema transaction preparation%% schema_update      Dump initiated during schema transaction commit%% fast_schema_update A schema_update, but ignores the log file%% user		      Dump initiated by user%% write_threshold    Automatic dump caused by too many log writes%% time_threshold     Automatic dump caused by timeout%% Public interface-export([	 get_log_writes/0,	 incr_log_writes/0,	 raw_dump_table/2,	 raw_named_dump_table/2,	 start_regulator/0,	 opt_dump_log/1,	 update/3	]). %% Internal stuff-export([regulator_init/1]).	-include("mnesia.hrl").-include_lib("kernel/include/file.hrl").-import(mnesia_lib, [fatal/2, dbg_out/2]).-define(REGULATOR_NAME, mnesia_dumper_load_regulator).-define(DumpToEtsMultiplier, 4).get_log_writes() ->    Max = mnesia_monitor:get_env(dump_log_write_threshold),    Prev = mnesia_lib:read_counter(trans_log_writes),    Left = mnesia_lib:read_counter(trans_log_writes_left),    Diff = Max - Left,    Prev + Diff.incr_log_writes() ->    Left = mnesia_lib:incr_counter(trans_log_writes_left, -1),    if	Left > 0 ->	    ignore;	true ->	    adjust_log_writes(true)    end.adjust_log_writes(DoCast) ->    Token = {mnesia_adjust_log_writes, self()},    case global:set_lock(Token, [node()], 1) of 	false ->	    ignore; %% Somebody else is sending a dump request	true -> 	    case DoCast of 		false ->		    ignore;		true -> 		    mnesia_controller:async_dump_log(write_threshold)	    end,	    Max = mnesia_monitor:get_env(dump_log_write_threshold),	    Left = mnesia_lib:read_counter(trans_log_writes_left),	    %% Don't care if we lost a few writes	    mnesia_lib:set_counter(trans_log_writes_left, Max),	    Diff = Max - Left,	    mnesia_lib:incr_counter(trans_log_writes, Diff),	    global:del_lock(Token, [node()])    end.%% Returns 'ok' or exitsopt_dump_log(InitBy) ->    Reg = case whereis(?REGULATOR_NAME) of	      undefined ->		  nopid; 	      Pid when pid(Pid) ->		  Pid 	  end,    perform_dump(InitBy, Reg).%% Scan for decisionsperform_dump(InitBy, Regulator) when InitBy == scan_decisions ->    ?eval_debug_fun({?MODULE, perform_dump}, [InitBy]),        dbg_out("Transaction log dump initiated by ~w~n", [InitBy]),    scan_decisions(mnesia_log:previous_log_file(), InitBy, Regulator),    scan_decisions(mnesia_log:latest_log_file(), InitBy, Regulator);%% Propagate the log into the DAT-filesperform_dump(InitBy, Regulator) ->    ?eval_debug_fun({?MODULE, perform_dump}, [InitBy]),    LogState = mnesia_log:prepare_log_dump(InitBy),    dbg_out("Transaction log dump initiated by ~w: ~w~n",	    [InitBy, LogState]),     adjust_log_writes(false),        mnesia_recover:allow_garb(),    case LogState of	already_dumped ->	    dumped;	{needs_dump, Diff} ->	    U = mnesia_monitor:get_env(dump_log_update_in_place),	    Cont = mnesia_log:init_log_dump(),	    case catch do_perform_dump(Cont, U, InitBy, Regulator, undefined) of		ok ->		    ?eval_debug_fun({?MODULE, post_dump}, [InitBy]),		    case mnesia_monitor:use_dir() of			true ->			    mnesia_recover:dump_decision_tab();			false ->			    mnesia_log:purge_some_logs()		    end,		    %% And now to the crucial point...		    mnesia_log:confirm_log_dump(Diff);		{error, Reason} ->		    {error, Reason}; 		{'EXIT', {Desc, Reason}} ->		    case mnesia_monitor:get_env(auto_repair) of			true ->			    mnesia_lib:important(Desc, Reason),			    %% Ignore rest of the log			    mnesia_log:confirm_log_dump(Diff);			false -> 			    fatal(Desc, Reason)		    end	    end;	{error, Reason} ->	    {error, {"Cannot prepare log dump", Reason}}    end.scan_decisions(Fname, InitBy, Regulator) ->    Exists = mnesia_lib:exists(Fname),    case Exists of	false ->	    ok;	true ->	    Header = mnesia_log:trans_log_header(),	    Name = previous_log,	    mnesia_log:open_log(Name, Header, Fname, Exists,				mnesia_monitor:get_env(auto_repair), read_only),	    Cont = start,	    Res = (catch do_perform_dump(Cont, false, InitBy, Regulator, undefined)),	    mnesia_log:close_log(Name),	    case Res of		ok -> ok;		{'EXIT', Reason} -> {error, Reason}	    end    end.do_perform_dump(Cont, InPlace, InitBy, Regulator, OldVersion) ->    case mnesia_log:chunk_log(Cont) of	{C2, Recs} ->	    case catch insert_recs(Recs, InPlace, InitBy, Regulator, OldVersion) of		{'EXIT', R} ->		    Reason = {"Transaction log dump error: ~p~n", [R]},		    close_files(InPlace, {error, Reason}, InitBy),		    exit(Reason);		Version ->		    do_perform_dump(C2, InPlace, InitBy, Regulator, Version)	    end;	eof ->	    close_files(InPlace, ok, InitBy),	    erase(mnesia_dumper_dets),	    ok    end.insert_recs([Rec | Recs], InPlace, InitBy, Regulator, LogV) ->    regulate(Regulator),    case insert_rec(Rec, InPlace, InitBy, LogV) of 	LogH when record(LogH, log_header) ->	    	    insert_recs(Recs, InPlace, InitBy, Regulator, LogH#log_header.log_version);	_ -> 	    insert_recs(Recs, InPlace, InitBy, Regulator, LogV)    end;insert_recs([], _InPlace, _InitBy, _Regulator, Version) ->    Version.insert_rec(Rec, _InPlace, scan_decisions, _LogV) ->    if 	record(Rec, commit) ->	    ignore;	record(Rec, log_header) ->	    ignore;	true ->	    mnesia_recover:note_log_decision(Rec, scan_decisions)    end;insert_rec(Rec, InPlace, InitBy, LogV) when record(Rec, commit) ->    %% Determine the Outcome of the transaction and recover it    D = Rec#commit.decision,    case mnesia_recover:wait_for_decision(D, InitBy) of	{Tid, committed} ->	    do_insert_rec(Tid, Rec, InPlace, InitBy, LogV);	{Tid, aborted} ->	    mnesia_schema:undo_prepare_commit(Tid, Rec)    end;insert_rec(H, _InPlace, _InitBy, _LogV) when record(H, log_header) ->    CurrentVersion = mnesia_log:version(),    if        H#log_header.log_kind /= trans_log ->	    exit({"Bad kind of transaction log", H});	H#log_header.log_version == CurrentVersion ->	    ok; 	H#log_header.log_version == "4.2" ->	    ok;	H#log_header.log_version == "4.1" ->	    ok;	H#log_header.log_version == "4.0" ->	    ok;	true ->	    fatal("Bad version of transaction log: ~p~n", [H])    end,    H;insert_rec(_Rec, _InPlace, _InitBy, _LogV) ->    ok.do_insert_rec(Tid, Rec, InPlace, InitBy, LogV) ->    case Rec#commit.schema_ops of	[] ->	    ignore;	SchemaOps ->	    case val({schema, storage_type}) of 		ram_copies -> 		    insert_ops(Tid, schema_ops, SchemaOps, InPlace, InitBy, LogV);	        Storage ->		    true = open_files(schema, Storage, InPlace, InitBy),		    insert_ops(Tid, schema_ops, SchemaOps, InPlace, InitBy, LogV)	    end    end,    D = Rec#commit.disc_copies,    insert_ops(Tid, disc_copies, D, InPlace, InitBy, LogV),    case InitBy of	startup ->	    DO = Rec#commit.disc_only_copies,	    insert_ops(Tid, disc_only_copies, DO, InPlace, InitBy, LogV);	_ ->	    ignore    end.    update(_Tid, [], _DumperMode) ->    dumped;update(Tid, SchemaOps, DumperMode) ->    UseDir = mnesia_monitor:use_dir(),    Res = perform_update(Tid, SchemaOps, DumperMode, UseDir),        mnesia_controller:release_schema_commit_lock(),    Res.perform_update(_Tid, _SchemaOps, mandatory, true) ->    %% Force a dump of the transaction log in order to let the    %% dumper perform needed updates    InitBy = schema_update,    ?eval_debug_fun({?MODULE, dump_schema_op}, [InitBy]),        opt_dump_log(InitBy);perform_update(Tid, SchemaOps, _DumperMode, _UseDir) ->    %% No need for a full transaction log dump.    %% Ignore the log file and perform only perform    %% the corresponding updates.    InitBy = fast_schema_update,     InPlace = mnesia_monitor:get_env(dump_log_update_in_place),    ?eval_debug_fun({?MODULE, dump_schema_op}, [InitBy]),    case catch insert_ops(Tid, schema_ops, SchemaOps, InPlace, InitBy, 			  mnesia_log:version()) of	{'EXIT', Reason} ->	    Error = {error, {"Schema update error", Reason}},	    close_files(InPlace, Error, InitBy),            fatal("Schema update error ~p ~p", [Reason, SchemaOps]);	_ ->		    	    ?eval_debug_fun({?MODULE, post_dump}, [InitBy]),	    close_files(InPlace, ok, InitBy),	    ok    end.insert_ops(_Tid, _Storage, [], _InPlace, _InitBy, _) ->    ok;insert_ops(Tid, Storage, [Op], InPlace, InitBy, Ver)  when Ver >= "4.3"->    insert_op(Tid, Storage, Op, InPlace, InitBy),    ok;insert_ops(Tid, Storage, [Op | Ops], InPlace, InitBy, Ver)  when Ver >= "4.3"->    insert_op(Tid, Storage, Op, InPlace, InitBy),    insert_ops(Tid, Storage, Ops, InPlace, InitBy, Ver);insert_ops(Tid, Storage, [Op | Ops], InPlace, InitBy, Ver) when Ver < "4.3" ->    insert_ops(Tid, Storage, Ops, InPlace, InitBy, Ver),    insert_op(Tid, Storage, Op, InPlace, InitBy).%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Normal opsdisc_insert(_Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy) ->    case open_files(Tab, Storage, InPlace, InitBy) of	true ->	    case Storage of 		disc_copies when Tab /= schema ->		    mnesia_log:append({?MODULE,Tab}, {{Tab, Key}, Val, Op}),		    ok;		_ ->		    dets_insert(Op,Tab,Key,Val)	    end;	false ->	    ignore    end.%% To fix update_counter so that it behaves better.%% i.e. if nothing have changed in tab except update_counter%% trust that the value in the dets file is correct. %% Otherwise we will get a double increment.%% This is perfect but update_counter is a dirty op.dets_insert(Op,Tab,Key,Val) ->    case Op of	write ->	    dets_updated(Tab,Key),	    ok = dets:insert(Tab, Val);	delete ->	    dets_updated(Tab,Key),	    ok = dets:delete(Tab, Key);	update_counter ->	    case dets_incr_counter(Tab,Key) of		true ->		    {RecName, Incr} = Val,		    case catch dets:update_counter(Tab, Key, Incr) of			CounterVal when integer(CounterVal) ->			    ok;			_ when Incr < 0 ->			    Zero = {RecName, Key, 0},			    ok = dets:insert(Tab, Zero);			_ -> 			    Init = {RecName, Key, Incr},			    ok = dets:insert(Tab, Init)		    end;		false ->  ok	    end;				    	delete_object ->	    dets_updated(Tab,Key),	    ok = dets:delete_object(Tab, Val);	clear_table ->	    dets_cleared(Tab),	    ok = dets:match_delete(Tab, '_')    end.	    dets_updated(Tab,Key) ->     case get(mnesia_dumper_dets) of	undefined -> 	    Empty = gb_trees:empty(),	    Tree = gb_trees:insert(Tab, gb_sets:singleton(Key), Empty),	    put(mnesia_dumper_dets, Tree);	Tree ->	    case gb_trees:lookup(Tab,Tree) of		{value, cleared} -> ignore;		{value, Set} -> 		    T = gb_trees:update(Tab, gb_sets:add(Key, Set), Tree),		    put(mnesia_dumper_dets, T);		none ->		    T = gb_trees:insert(Tab, gb_sets:singleton(Key), Tree),		    put(mnesia_dumper_dets, T)	    end    end.dets_incr_counter(Tab,Key) ->    case get(mnesia_dumper_dets) of	undefined -> false;	Tree ->	    case gb_trees:lookup(Tab,Tree) of		{value, cleared} -> true;		{value, Set} -> gb_sets:is_member(Key, Set);		none -> false	    end    end.dets_cleared(Tab) ->    case get(mnesia_dumper_dets) of	undefined -> 	    Empty = gb_trees:empty(),	    Tree = gb_trees:insert(Tab, cleared, Empty),	    put(mnesia_dumper_dets, Tree);	Tree ->	    case gb_trees:lookup(Tab,Tree) of		{value, cleared} -> ignore;		_ -> 		    T = gb_trees:enter(Tab, cleared, Tree),		    put(mnesia_dumper_dets, T)	    end    end.insert(Tid, Storage, Tab, Key, [Val | Tail], Op, InPlace, InitBy) ->    insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy),

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?