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