mnesia_bup.erl

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

ERL
1,170
字号
%% ``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_bup).-export([	 %% Public interface	 iterate/4,	 read_schema/2,	 fallback_bup/0,	 fallback_exists/0,	 tm_fallback_start/1,	 create_schema/1,	 install_fallback/1,	 install_fallback/2,	 uninstall_fallback/0,	 uninstall_fallback/1,	 traverse_backup/4,	 traverse_backup/6,	 make_initial_backup/3,	 fallback_to_schema/0,	 lookup_schema/2,	 schema2bup/1,	 refresh_cookie/2,	 %% Internal	 fallback_receiver/2,	 install_fallback_master/2,	 uninstall_fallback_master/2,	 local_uninstall_fallback/2,	 do_traverse_backup/7,	 trav_apply/4	]).-include("mnesia.hrl").-import(mnesia_lib, [verbose/2, dbg_out/2]).-record(restore, {mode, bup_module, bup_data}).-record(fallback_args, {opaque,			scope = global,			module = mnesia_monitor:get_env(backup_module),			use_default_dir = true,			mnesia_dir,			fallback_bup,			fallback_tmp,			skip_tables = [],			keep_tables = [],			default_op = keep_tables		       		       }).%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Backup iterator%% Reads schema section and iterates over all records in a backup.%%%% Fun(BunchOfRecords, Header, Schema, Acc) is applied when a suitable amount%% of records has been collected.%%%% BunchOfRecords will be [] when the iteration is done.iterate(Mod, Fun, Opaque, Acc) ->    R = #restore{bup_module = Mod, bup_data = Opaque},    case catch read_schema_section(R) of	{error, Reason} ->	    {error, Reason};	{R2, {Header, Schema, Rest}} ->	    case catch iter(R2, Header, Schema, Fun, Acc, Rest) of		{ok, R3, Res} ->		    catch safe_apply(R3, close_read, [R3#restore.bup_data]),		    {ok, Res};		{error, Reason} ->		    catch safe_apply(R2, close_read, [R2#restore.bup_data]),		    {error, Reason};		{'EXIT', Pid, Reason} ->		    catch safe_apply(R2, close_read, [R2#restore.bup_data]),		    {error, {'EXIT', Pid, Reason}};		{'EXIT', Reason} ->		    catch safe_apply(R2, close_read, [R2#restore.bup_data]),		    {error, {'EXIT', Reason}}	    end    end.iter(R, Header, Schema, Fun, Acc, []) ->    case safe_apply(R, read, [R#restore.bup_data]) of	{R2, []} ->	    Res = Fun([], Header, Schema, Acc),	    {ok, R2, Res};	{R2, BupItems} ->	    iter(R2, Header, Schema, Fun, Acc, BupItems)    end;iter(R, Header, Schema, Fun, Acc, BupItems) ->    Acc2 = Fun(BupItems, Header, Schema, Acc),    iter(R, Header, Schema, Fun, Acc2, []).safe_apply(R, write, [_, Items]) when Items == [] ->    R;safe_apply(R, What, Args) ->    Abort = fun(Re) -> abort_restore(R, What, Args, Re) end,    receive	{'EXIT', Pid, Re} -> Abort({'EXIT', Pid, Re})    after 0 ->	    Mod = R#restore.bup_module,	    case catch apply(Mod, What, Args) of		{ok, Opaque, Items} when What == read ->		    {R#restore{bup_data = Opaque}, Items};		{ok, Opaque}  when What /= read->		    R#restore{bup_data = Opaque};		{error, Re} ->		    Abort(Re);		Re ->		    Abort(Re)	    end    end.abort_restore(R, What, Args, Reason) ->    Mod = R#restore.bup_module,    Opaque = R#restore.bup_data,    dbg_out("Restore aborted. ~p:~p~p -> ~p~n",	    [Mod, What, Args, Reason]),    catch apply(Mod, close_read, [Opaque]),    throw({error, Reason}).	    fallback_to_schema() ->    Fname = fallback_bup(),    fallback_to_schema(Fname).fallback_to_schema(Fname) ->    Mod = mnesia_backup,    case read_schema(Mod, Fname) of	{error, Reason} ->	    {error, Reason};	Schema ->	    case catch lookup_schema(schema, Schema) of		{error, _} -> 		    {error, "No schema in fallback"};		List ->		    {ok, fallback, List}	    end    end.%% Opens Opaque reads schema and then close read_schema(Mod, Opaque) ->    R = #restore{bup_module = Mod, bup_data = Opaque},    case catch read_schema_section(R) of	{error, Reason} ->	    {error, Reason};	{R2, {_Header, Schema, _}} ->	    catch safe_apply(R2, close_read, [R2#restore.bup_data]),	    Schema    end.    %% Open backup media and extract schema%% rewind backup media and leave it open%% Returns {R, {Header, Schema}}read_schema_section(R) ->    case catch do_read_schema_section(R) of	{'EXIT', Reason} ->	    catch safe_apply(R, close_read, [R#restore.bup_data]),	    {error, {'EXIT', Reason}};	{error, Reason} ->	    catch safe_apply(R, close_read, [R#restore.bup_data]),	    {error, Reason};	{R2, {H, Schema, Rest}} ->	    Schema2 = convert_schema(H#log_header.log_version, Schema),	    {R2, {H, Schema2, Rest}}    end.do_read_schema_section(R) ->    R2 = safe_apply(R, open_read, [R#restore.bup_data]),    {R3, RawSchema} = safe_apply(R2, read, [R2#restore.bup_data]),    do_read_schema_section(R3, verify_header(RawSchema), []).do_read_schema_section(R, {ok, B, C, []}, Acc) ->    case safe_apply(R, read, [R#restore.bup_data]) of	{R2, []} ->	    {R2, {B, Acc, []}};	{R2, RawSchema} ->	    do_read_schema_section(R2, {ok, B, C, RawSchema}, Acc)    end;do_read_schema_section(R, {ok, B, C, [Head | Tail]}, Acc)        when element(1, Head) == schema ->    do_read_schema_section(R, {ok, B, C, Tail}, Acc ++ [Head]);do_read_schema_section(R, {ok, B, _C, Rest}, Acc) ->    {R, {B, Acc, Rest}};do_read_schema_section(_R, {error, Reason}, _Acc) ->    {error, Reason}.verify_header([H | RawSchema])  when record(H, log_header) ->    Current = mnesia_log:backup_log_header(),    if	H#log_header.log_kind == Current#log_header.log_kind ->	    Versions = ["0.1", "1.1", Current#log_header.log_version],	    case lists:member(H#log_header.log_version, Versions) of		true ->		    {ok, H, Current, RawSchema};		false ->		    {error, {"Bad header version. Cannot be used as backup.", H}}	    end;	true ->	    {error, {"Bad kind of header. Cannot be used as backup.", H}}    end;verify_header(RawSchema) ->    {error, {"Missing header. Cannot be used as backup.", catch hd(RawSchema)}}.refresh_cookie(Schema, NewCookie) ->    case lists:keysearch(schema, 2, Schema) of	{value, {schema, schema, List}} ->	    Cs = mnesia_schema:list2cs(List),	    Cs2 = Cs#cstruct{cookie = NewCookie},	    Item = {schema, schema, mnesia_schema:cs2list(Cs2)},	    lists:keyreplace(schema, 2, Schema, Item);		false ->	    Reason = "No schema found. Cannot be used as backup.",	    throw({error, {Reason, Schema}})    end.%% Convert schema items from an external backup%% If backup format is the latest, no conversion is needed%% All supported backup formats should have their converters%% here as separate function clauses.convert_schema("0.1", Schema) ->    convert_0_1(Schema);convert_schema("1.1", Schema) ->    %% The new backup format is a pure extension of the old one    Current = mnesia_log:backup_log_header(),    convert_schema(Current#log_header.log_version, Schema);convert_schema(Latest, Schema) ->    H = mnesia_log:backup_log_header(),    if	H#log_header.log_version == Latest ->	    Schema;	true ->	    Reason = "Bad backup header version. Cannot convert schema.",	    throw({error, {Reason, H}})    end.%% Backward compatibility for 0.1convert_0_1(Schema) ->    case lists:keysearch(schema, 2, Schema) of	{value, {schema, schema, List}} ->	    Schema2 = lists:keydelete(schema, 2, Schema),	    Cs = mnesia_schema:list2cs(List),	    convert_0_1(Schema2, [], Cs);	false ->	    List = mnesia_schema:get_initial_schema(disc_copies, [node()]),	    Cs = mnesia_schema:list2cs(List),	    convert_0_1(Schema, [], Cs)    end.convert_0_1([{schema, cookie, Cookie} | Schema], Acc, Cs) ->    convert_0_1(Schema, Acc, Cs#cstruct{cookie = Cookie});convert_0_1([{schema, db_nodes, DbNodes} | Schema], Acc, Cs) ->    convert_0_1(Schema, Acc, Cs#cstruct{disc_copies = DbNodes});convert_0_1([{schema, version, Version} | Schema], Acc, Cs) ->    convert_0_1(Schema, Acc, Cs#cstruct{version = Version});convert_0_1([{schema, Tab, Def} | Schema], Acc, Cs) ->    Head = 	case lists:keysearch(index, 1, Def) of	    {value, {index, PosList}} ->		%% Remove the snmp "index"		P = PosList -- [snmp],		Def2 = lists:keyreplace(index, 1, Def, {index, P}),		{schema, Tab, Def2};	    false ->		{schema, Tab, Def}	end,    convert_0_1(Schema, [Head | Acc], Cs);convert_0_1([Head | Schema], Acc, Cs) ->    convert_0_1(Schema, [Head | Acc], Cs);convert_0_1([], Acc, Cs) ->    [schema2bup({schema, schema, Cs}) | Acc].%% Returns Val or throw errorlookup_schema(Key, Schema) ->    case lists:keysearch(Key, 2, Schema) of	{value, {schema, Key, Val}} -> Val;	false -> throw({error, {"Cannot lookup", Key}})    end.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Backup compatibility%% Convert internal schema items to backup ditoschema2bup({schema, Tab}) ->    {schema, Tab};schema2bup({schema, Tab, TableDef}) ->    {schema, Tab, mnesia_schema:cs2list(TableDef)}.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Create schema on the given nodes%% Requires that old schemas has been deleted%% Returns ok | {error, Reason}create_schema([]) ->    create_schema([node()]);create_schema(Ns) when list(Ns) ->    case is_set(Ns) of	true ->	    create_schema(Ns, mnesia_schema:ensure_no_schema(Ns));	false ->	    {error, {combine_error, Ns}}    end;create_schema(Ns) ->    {error, {badarg, Ns}}.is_set(List) when list(List) ->    ordsets:is_set(lists:sort(List));is_set(_) ->    false.create_schema(Ns, ok) ->    %% Ensure that we access the intended Mnesia    %% directory. This function may not be called    %% during startup since it will cause the    %% application_controller to get into deadlock    case mnesia_lib:ensure_loaded(?APPLICATION) of	ok ->	    case mnesia_monitor:get_env(schema_location) of		ram -> 		    {error, {has_no_disc, node()}};		_ ->		    case mnesia_schema:opt_create_dir(true, mnesia_lib:dir()) of			{error, What} ->			    {error, What};			ok ->			    Mod = mnesia_backup,			    Str = mk_str(),			    File = mnesia_lib:dir(Str),			    file:delete(File),			    case catch make_initial_backup(Ns, File, Mod) of				{ok, _Res} ->				    case do_install_fallback(File, Mod) of					ok ->					    file:delete(File),					    ok;					{error, Reason} ->					    {error, Reason}				    end;				{error, Reason} ->				    {error, Reason}			    end		    end	    end;		{error, Reason} ->	    {error, Reason}    end;create_schema(_Ns, {error, Reason}) ->    {error, Reason};create_schema(_Ns, Reason) ->    {error, Reason}.mk_str() ->    Now = [integer_to_list(I) || I <- tuple_to_list(now())],    lists:concat([node()] ++ Now ++ ".TMP").make_initial_backup(Ns, Opaque, Mod) ->    Schema = [{schema, schema, mnesia_schema:get_initial_schema(disc_copies, Ns)}],    O2 = do_apply(Mod, open_write, [Opaque], Opaque),    O3 = do_apply(Mod, write, [O2, [mnesia_log:backup_log_header()]], O2),    O4 = do_apply(Mod, write, [O3, Schema], O3),    O5 = do_apply(Mod, commit_write, [O4], O4),    {ok, O5}.do_apply(_, write, [_, Items], Opaque) when Items == [] ->    Opaque;do_apply(Mod, What, Args, _Opaque) ->    case catch apply(Mod, What, Args) of	{ok, Opaque2} ->  Opaque2;	{error, Reason} -> throw({error, Reason});	{'EXIT', Reason} -> throw({error, {'EXIT', Reason}})    end.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Restore

⌨️ 快捷键说明

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