mnesia_log.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,023 行 · 第 1/2 页
ERL
1,023 行
%% ``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$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% This module administers three kinds of log files:%%%% 1 The transaction log%% mnesia_tm appends to the log (via mnesia_log) at the%% end of each transaction (or dirty write) and%% mnesia_dumper reads the log and performs the ops in%% the dat files. The dump_log is done at startup and%% at intervals controlled by the user.%%%% 2 The mnesia_down log%% mnesia_tm appends to the log (via mnesia_log) when it%% realizes that mnesia goes up or down on another node.%% mnesia_init reads the log (via mnesia_log) at startup.%%%% 3 The backup log%% mnesia_schema produces one tiny log when the schema is%% initially created. mnesia_schema also reads the log%% when the user wants tables (possibly incl the schema)%% to be restored. mnesia_log appends to the log when the%% user wants to produce a real backup.%%%% The actual access to the backup media is performed via the%% mnesia_backup module for both read and write. mnesia_backup%% uses the disk_log (*), BUT the user may write an own module%% with the same interface as mnesia_backup and configure%% Mnesia so the alternate module performs the actual accesses%% to the backup media. This means that the user may put the%% backup on medias that Mnesia does not know about possibly on%% hosts where Erlang is not running.%%%% All these logs have to some extent a common structure.%% They are all using the disk_log module (*) for the basic%% file structure. The disk_log has a repair feature that%% can be used to skip erroneous log records if one comes to%% the conclusion that it is more important to reuse some%% of the log records than the risque of obtaining inconsistent%% data. If the data becomes inconsistent it is solely up to the%% application to make it consistent again. The automatic%% reparation of the disk_log is very powerful, but use it%% with extreme care.%%%% First in all Mnesia's log file is a mnesia log header.%% It contains a list with a log_header record as single%% element. The structure of the log_header may never be%% changed since it may be written to very old backup files.%% By holding this record definition stable we can be%% able to comprahend backups from timepoint 0. It also%% allows us to use the backup format as an interchange%% format between Mnesia releases.%%%% An op-list is a list of tuples with arity 3. Each tuple%% has this structure: {Oid, Recs, Op} where Oid is the tuple%% {Tab, Key}, Recs is a (possibly empty) list of records and%% Op is an atom.%%%% The log file structure for the transaction log is as follows.%%%% After the mnesia log section follows an extended record section%% containing op-lists. There are several values that Op may%% have, such as write, delete, update_counter, delete_object,%% and replace. There is no special end of section marker.%%%% +-----------------+%% | mnesia log head |%% +-----------------+%% | extended record |%% | section |%% +-----------------+%%%% The log file structure for the mnesia_down log is as follows.%%%% After the mnesia log section follows a mnesia_down section%% containg lists with yoyo records as single element.%%%% +-----------------+%% | mnesia log head |%% +-----------------+%% | mnesia_down |%% | section |%% +-----------------+%%%% The log file structure for the backup log is as follows.%%%% After the mnesia log section follows a schema section%% containing record lists. A record list is a list of tuples%% where {schema, Tab} is interpreted as a delete_table(Tab) and%% {schema, Tab, CreateList} are interpreted as create_table.%%%% The record section also contains record lists. In this section%% {Tab, Key} is interpreted as delete({Tab, Key}) and other tuples%% as write(Tuple). There is no special end of section marker.%%%% +-----------------+%% | mnesia log head |%% +-----------------+%% | schema section |%% +-----------------+%% | record section |%% +-----------------+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%-module(mnesia_log).-export([ append/2, backup/1, backup/2, backup_checkpoint/2, backup_checkpoint/3, backup_log_header/0, backup_master/2, chunk_decision_log/1, chunk_decision_tab/1, chunk_log/1, chunk_log/2, close_decision_log/0, close_decision_tab/0, close_log/1, unsafe_close_log/1, confirm_log_dump/1, confirm_decision_log_dump/0, previous_log_file/0, previous_decision_log_file/0, latest_log_file/0, decision_log_version/0, decision_log_file/0, decision_tab_file/0, decision_tab_version/0, dcl_version/0, dcd_version/0, ets2dcd/1, ets2dcd/2, dcd2ets/1, dcd2ets/2, init/0, init_log_dump/0, log/1, slog/1, log_decision/1, log_files/0, open_decision_log/0, trans_log_header/0, open_decision_tab/0, dcl_log_header/0, dcd_log_header/0, open_log/4, open_log/6, prepare_decision_log_dump/0, prepare_log_dump/1, save_decision_tab/1, purge_all_logs/0, purge_some_logs/0, stop/0, tab_copier/3, version/0, view/0, view/1, write_trans_log_header/0 ]). -include("mnesia.hrl").-import(mnesia_lib, [val/1, dir/1]).-import(mnesia_lib, [exists/1, fatal/2, error/2, dbg_out/2]).trans_log_header() -> log_header(trans_log, version()).backup_log_header() -> log_header(backup_log, "1.2").decision_log_header() -> log_header(decision_log, decision_log_version()).decision_tab_header() -> log_header(decision_tab, decision_tab_version()).dcl_log_header() -> log_header(dcl_log, dcl_version()).dcd_log_header() -> log_header(dcd_log, dcd_version()).log_header(Kind, Version) -> #log_header{log_version=Version, log_kind=Kind, mnesia_version=mnesia:system_info(version), node=node(), now=now()}.version() -> "4.3".decision_log_version() -> "3.0".decision_tab_version() -> "1.0".dcl_version() -> "1.0".dcd_version() -> "1.0". append(Log, Bin) when binary(Bin) -> disk_log:balog(Log, Bin);append(Log, Term) -> disk_log:alog(Log, Term).%% Synced appendsappend(Log, Bin) when binary(Bin) -> ok = disk_log:blog(Log, Bin); sappend(Log, Term) -> ok = disk_log:log(Log, Term). %% Write commit records to the latest_loglog(C) when C#commit.disc_copies == [], C#commit.disc_only_copies == [], C#commit.schema_ops == [] -> ignore;log(C) -> case mnesia_monitor:use_dir() of true -> if record(C, commit) -> C2 = C#commit{ram_copies = [], snmp = []}, append(latest_log, C2); true -> %% Either a commit record as binary %% or some decision related info append(latest_log, C) end, mnesia_dumper:incr_log_writes(); false -> ignore end.%% Syncedslog(C) when C#commit.disc_copies == [], C#commit.disc_only_copies == [], C#commit.schema_ops == [] -> ignore;slog(C) -> case mnesia_monitor:use_dir() of true -> if record(C, commit) -> C2 = C#commit{ram_copies = [], snmp = []}, sappend(latest_log, C2); true -> %% Either a commit record as binary %% or some decision related info sappend(latest_log, C) end, mnesia_dumper:incr_log_writes(); false -> ignore end.%% Stuff related to the file LOG%% Returns a list of logfiles. The oldest is first.log_files() -> [previous_log_file(), latest_log_file(), decision_tab_file() ].latest_log_file() -> dir(latest_log_name()).previous_log_file() -> dir("PREVIOUS.LOG").decision_log_file() -> dir(decision_log_name()).decision_tab_file() -> dir(decision_tab_name()). previous_decision_log_file() -> dir("PDECISION.LOG").latest_log_name() -> "LATEST.LOG".decision_log_name() -> "DECISION.LOG".decision_tab_name() -> "DECISION_TAB.LOG".init() -> case mnesia_monitor:use_dir() of true -> Prev = previous_log_file(), verify_no_exists(Prev), Latest = latest_log_file(), verify_no_exists(Latest), Header = trans_log_header(), open_log(latest_log, Header, Latest); false -> ok end.verify_no_exists(Fname) -> case exists(Fname) of false -> ok; true -> fatal("Log file exists: ~p~n", [Fname]) end.open_log(Name, Header, Fname) -> Exists = exists(Fname), open_log(Name, Header, Fname, Exists).open_log(Name, Header, Fname, Exists) -> Repair = mnesia_monitor:get_env(auto_repair), open_log(Name, Header, Fname, Exists, Repair).open_log(Name, Header, Fname, Exists, Repair) -> case Name == previous_log of true -> open_log(Name, Header, Fname, Exists, Repair, read_only); false -> open_log(Name, Header, Fname, Exists, Repair, read_write) end.open_log(Name, Header, Fname, Exists, Repair, Mode) -> Args = [{file, Fname}, {name, Name}, {repair, Repair}, {mode, Mode}],%% io:format("~p:open_log: ~p ~p~n", [?MODULE, Name, Fname]), case mnesia_monitor:open_log(Args) of {ok, Log} when Exists == true -> Log; {ok, Log} -> write_header(Log, Header), Log; {repaired, Log, _, {badbytes, 0}} when Exists == true -> Log; {repaired, Log, _, {badbytes, 0}} -> write_header(Log, Header), Log; {repaired, Log, _Recover, BadBytes} -> mnesia_lib:important("Data may be missing, log ~p repaired: Lost ~p bytes~n", [Fname, BadBytes]), Log; {error, Reason} when Repair == true -> file:delete(Fname), mnesia_lib:important("Data may be missing, Corrupt logfile deleted: ~p, ~p ~n", [Fname, Reason]), %% Create a new open_log(Name, Header, Fname, false, false, read_write); {error, Reason} -> fatal("Cannot open log file ~p: ~p~n", [Fname, Reason]) end.write_header(Log, Header) -> append(Log, Header).write_trans_log_header() -> write_header(latest_log, trans_log_header()).stop() -> case mnesia_monitor:use_dir() of true -> close_log(latest_log); false -> ok end.close_log(Log) ->%% io:format("mnesia_log:close_log ~p~n", [Log]),%% io:format("mnesia_log:close_log ~p~n", [Log]), case disk_log:sync(Log) of ok -> ok; {error, {read_only_mode, Log}} -> ok; {error, Reason} -> mnesia_lib:important("Failed syncing ~p to_disk reason ~p ~n", [Log, Reason]) end, mnesia_monitor:close_log(Log).unsafe_close_log(Log) ->%% io:format("mnesia_log:close_log ~p~n", [Log]), mnesia_monitor:unsafe_close_log(Log).purge_some_logs() -> mnesia_monitor:unsafe_close_log(latest_log), file:delete(latest_log_file()), file:delete(decision_tab_file()).purge_all_logs() -> file:delete(previous_log_file()), file:delete(latest_log_file()), file:delete(decision_tab_file()).%% Prepare dump by renaming the open logfile if possible%% Returns a tuple on the following format: {Res, OpenLog}%% where OpenLog is the file descriptor to log file, ready for append%% and Res is one of the following: already_dumped, needs_dump or {error, Reason}prepare_log_dump(InitBy) -> Diff = mnesia_dumper:get_log_writes() - mnesia_lib:read_counter(trans_log_writes_prev), if Diff == 0, InitBy /= startup -> already_dumped; true -> case mnesia_monitor:use_dir() of true -> Prev = previous_log_file(), prepare_prev(Diff, InitBy, Prev, exists(Prev)); false -> already_dumped end end.prepare_prev(Diff, _, _, true) -> {needs_dump, Diff};prepare_prev(Diff, startup, Prev, false) -> Latest = latest_log_file(), case exists(Latest) of true -> case file:rename(Latest, Prev) of ok -> {needs_dump, Diff}; {error, Reason} -> {error, Reason} end; false -> already_dumped end;prepare_prev(Diff, _InitBy, Prev, false) -> Head = trans_log_header(), case mnesia_monitor:reopen_log(latest_log, Prev, Head) of ok -> {needs_dump, Diff}; {error, Reason} -> Latest = latest_log_file(), {error, {"Cannot rename log file", [Latest, Prev, Reason]}} end.%% Init dump and return PrevLogFileDesc or exit.init_log_dump() -> Fname = previous_log_file(), open_log(previous_log, trans_log_header(), Fname), start.chunk_log(Cont) -> chunk_log(previous_log, Cont).chunk_log(_Log, eof) -> eof;chunk_log(Log, Cont) -> case catch disk_log:chunk(Log, Cont) of {error, Reason} -> fatal("Possibly truncated ~p file: ~p~n", [Log, Reason]); {C2, Chunk, _BadBytes} -> %% Read_only case, should we warn about the bad log file? %% BUGBUG Should we crash if Repair == false ?? %% We got to check this !! mnesia_lib:important("~p repaired, lost ~p bad bytes~n", [Log, _BadBytes]), {C2, Chunk}; Other -> Other end.%% Confirms the dump by closing prev log and delete the fileconfirm_log_dump(Updates) -> case mnesia_monitor:close_log(previous_log) of ok -> file:delete(previous_log_file()), mnesia_lib:incr_counter(trans_log_writes_prev, Updates), dumped; {error, Reason} -> {error, Reason} end.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Decision logopen_decision_log() -> Latest = decision_log_file(), open_log(decision_log, decision_log_header(), Latest), start. prepare_decision_log_dump() -> Prev = previous_decision_log_file(), prepare_decision_log_dump(exists(Prev), Prev).prepare_decision_log_dump(false, Prev) -> Head = decision_log_header(), case mnesia_monitor:reopen_log(decision_log, Prev, Head) of ok -> prepare_decision_log_dump(true, Prev); {error, Reason} -> fatal("Cannot rename decision log file ~p -> ~p: ~p~n", [decision_log_file(), Prev, Reason]) end;prepare_decision_log_dump(true, Prev) -> open_log(previous_decision_log, decision_log_header(), Prev), start.chunk_decision_log(Cont) -> %% dbg_out("chunk log ~p~n", [Cont]), chunk_log(previous_decision_log, Cont).%% Confirms dump of the decision log
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?