From 43647249896ca972844796cdd8e23d485749a7ed Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Mon, 1 Aug 2011 18:38:17 +0200 Subject: [mnesia] Mnesia schema merge tested with 2 nodes, r13b04 -> r14b (dev) The do_merge_schema function now converts cstructs from a remote node when it detects that they are different. In order to be compatible the other way around, mnesia_controller:get_cstructs() detects a remote caller, and converts the cstructs before sending them. --- lib/mnesia/src/mnesia_controller.erl | 59 ++++++++++++++++++++++++++++++++++-- lib/mnesia/src/mnesia_schema.erl | 26 ++++++++++++---- 2 files changed, 77 insertions(+), 8 deletions(-) (limited to 'lib/mnesia/src') diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index d4b2c7b5cc..8a02fe0226 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -57,7 +57,8 @@ release_schema_commit_lock/0, create_table/1, get_disc_copy/1, - get_cstructs/0, + get_remote_cstructs/0, % new function + get_cstructs/0, % old function sync_and_block_table_whereabouts/4, sync_del_table_copy_whereabouts/2, block_table/1, @@ -278,9 +279,63 @@ rec_tabs([], _, _, Init) -> unlink(Init), ok. -get_cstructs() -> +%% New function that does exactly what get_cstructs() used to do. +%% When this function is called, we know that the calling node knows +%% how to convert cstructs on the receiving end (should they differ). +get_remote_cstructs() -> call(get_cstructs). +%% Old function kept for backwards compatibility; converts cstructs before sending. +get_cstructs() -> + {cstructs, Cstructs, Running} = Reply = call(get_cstructs), + case is_remote_call() of + {true, Node} -> + {cstructs, normalize_cstructs(Cstructs, Node), Running}; + false -> + Reply + end. + +is_remote_call() -> + case node(group_leader()) of + N when N =/= node() -> + {true, N}; + _ -> + false + end. + +normalize_cstructs(Cstructs, Node) -> + %% backward-compatibility hack; normalize before returning + case rpc:call(Node, mnesia_lib, val, [{schema,cstruct}]) of + {badrpc, _} -> + %% assume it's not a schema merge + Cstructs; + #cstruct{} -> + %% same format + Cstructs; + Cstruct -> + %% some other format + RemoteFields = [F || {F,_} <- rpc:call(Node, mnesia_schema, cs2list, [Cstruct])], + [convert_cs(Cs, RemoteFields) || Cs <- Cstructs] + end. + +convert_cs(Cs, Fields) -> + MyFields = record_info(fields, cstruct), + convert(tl(tuple_to_list(Cs)), MyFields, Fields, []). + +convert([H|T], [F|FsL], [F|FsR], Acc) -> + convert(T, FsL, FsR, [H|Acc]); +convert([H|T], [Fl|FsL] = L, [Fr|FsR] = R, Acc) -> + case {lists:member(Fl, FsR), lists:member(Fr, FsL)} of + {true, false} -> + convert(T, L, FsR, [H|Acc]); + {false, true} -> + %% Field Fl doesn't exist on receiver side; skip. + convert(T, FsL, R, Acc) + end; +convert([], _, _, Acc) -> + list_to_tuple([cstruct|lists:reverse(Acc)]). + + update(Fun) -> call({update,Fun}). diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index fef72ad39c..e5cea16f0c 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -2733,7 +2733,8 @@ do_merge_schema(LockTabs0) -> Tid, Store, T, mnesia_lib:intersect(Ns, OtherNodes)) || {T,Ns} <- LockTabs], case rpc:call(Node, mnesia_controller, get_cstructs, []) of - {cstructs, Cstructs, RemoteRunning1} -> + {cstructs, Cstructs0, RemoteRunning1} -> + {Cstructs, DeleteFields} = normalize_remote_cstructs(Cstructs0, Node), LockedAlready = Running ++ [Node], {New, Old} = mnesia_recover:connect_nodes(RemoteRunning1), RemoteRunning = mnesia_lib:intersect(New ++ Old, RemoteRunning1), @@ -2754,19 +2755,20 @@ do_merge_schema(LockTabs0) -> %% Announce that Node is running A = [{op, announce_im_running, node(), - cs2list(SchemaCs), Running, RemoteRunning}], + [{K,V} || {K,V} <- cs2list(SchemaCs), + not lists:member(K, DeleteFields)], Running, RemoteRunning}], do_insert_schema_ops(Store, A), - + %% Introduce remote tables to local node do_insert_schema_ops(Store, make_merge_schema(Node, Cstructs)), - + %% Introduce local tables to remote nodes Tabs = val({schema, tables}), Ops = [{op, merge_schema, get_create_list(T)} || T <- Tabs, not lists:keymember(T, #cstruct.name, Cstructs)], do_insert_schema_ops(Store, Ops), - + %% Ensure that the txn will be committed on all nodes NewNodes = RemoteRunning -- Running, mnesia_lib:set(prepare_op, {announce_im_running,NewNodes}), @@ -2782,6 +2784,18 @@ do_merge_schema(LockTabs0) -> not_merged end. +normalize_remote_cstructs([#cstruct{}|_] = Cstructs, _) -> + {Cstructs, []}; +normalize_remote_cstructs([T|_] = Cstructs, Node) when element(1,T) == cstruct -> + Flds = [F || {F, _} <- rpc:call(Node, ?MODULE, cs2list, [T])], + DeleteFields = record_info(fields, cstruct) -- Flds, + NewCstructs = lists:map( + fun(Cs) -> + CsL = lists:zip(Flds, tl(tuple_to_list(Cs))), + #cstruct{} = list2cs(CsL) + end, Cstructs), + {NewCstructs, DeleteFields}. + tab_to_nodes(Tab) when is_atom(Tab) -> Cs = val({Tab, cstruct}), mnesia_lib:cs_to_nodes(Cs). @@ -2919,7 +2933,7 @@ do_make_merge_schema(Node, RemoteCs) -> %% Choose remote cstruct, %% since it's the master [{op, merge_schema, cs2list(RemoteCs)}]; - + true -> Str = io_lib:format("Bad cookie in table definition" " ~w: ~w = ~w, ~w = ~w~n", -- cgit v1.2.3