Skip to content

Commit

Permalink
[mnesia] Mnesia schema merge tested with 2 nodes, r13b04 -> r14b (dev)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Ulf Wiger authored and dgud committed Sep 15, 2011
1 parent 2566dbc commit 4364724
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 8 deletions.
59 changes: 57 additions & 2 deletions lib/mnesia/src/mnesia_controller.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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}).

Expand Down
26 changes: 20 additions & 6 deletions lib/mnesia/src/mnesia_schema.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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}),
Expand All @@ -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).
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit 4364724

Please sign in to comment.