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 + -
显示快捷键?