Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ulf Wiger committed Feb 4, 2010
0 parents commit 649ad0b
Show file tree
Hide file tree
Showing 6 changed files with 424 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README
@@ -0,0 +1,12 @@
unsplit - Resolve conflicts in Mnesia tables after network split

Author: Ulf Wiger, Erlang Solutions Ltd


NOTE: This application is currently a fairly crude prototype,
and is not intended for mission-critical tasks (yet).

Documentation will follow, as soon as the approach has been
verified to work. So far, no Mnesia patches are used, but
for it to work really well, some added functionality in
Mnesia may well be needed.
26 changes: 26 additions & 0 deletions commands.txt
@@ -0,0 +1,26 @@
erl -boot start_sasl -kernel dist_auto_connect once -sname n1 -pa ~/src/unsplit-0.1/ebin -mnesia debug trace
erl -boot start_sasl -kernel dist_auto_connect once -sname n2 -pa ~/src/unsplit-0.1/ebin -mnesia debug trace

mnesia:start().
application:start(unsplit).
rd(test,{key,modified=erlang:now(),value}).

On n2@debian:

mnesia:create_schema([n1@debian,n2@debian]).

mnesia:delete_table(test).
mnesia:create_table(test,[{ram_copies,[n1@debian,n2@debian]},{attributes,[key,modified,value]},{user_properties,[{unsplit_method,{unsplit_lib,last_modified,[]}}]}]).

mnesia:transaction(fun() -> mnesia:write(#test{key=1,value=a}) end).
mnesia:transaction(fun() -> mnesia:write(#test{key=2,value=a}) end).

ets:tab2list(test).


On n1@debian:

disconnect_node(n2@debian).
mnesia:transaction(fun() -> mnesia:write(#test{key=2,value=b}) end).
timer:sleep(3000).
net_kernel:connect_node(n2@debian).
7 changes: 7 additions & 0 deletions ebin/unsplit.app
@@ -0,0 +1,7 @@
{application, unsplit,
[{vsn, "0.1"},
{description, "Merges mnesia tables after net split"},
{applications, [mnesia]},
{mod, {unsplit, []}},
{env, []}
]}.
22 changes: 22 additions & 0 deletions src/unsplit.erl
@@ -0,0 +1,22 @@
-module(unsplit).

-behaviour(application).
-behaviour(supervisor).

-export([start/2, stop/1]).


-export([init/1]).

start(_, _) ->
supervisor:start_link({local,?MODULE}, ?MODULE, []).

stop(_) ->
ok.

%% Supervisor callback:

init([]) ->
Children = [{unsplit_server, {unsplit_server, start_link, []},
permanent, 3000, worker, [unsplit_server]}],
{ok, {{one_for_one, 3, 10}, Children}}.
69 changes: 69 additions & 0 deletions src/unsplit_lib.erl
@@ -0,0 +1,69 @@
-module(unsplit_lib).
-export([no_action/2,
last_modified/2,
last_version/2]).



no_action(start, [Tab|_]) ->
error_logger:format("Will not merge table ~p~n", [Tab]),
stop.


last_modified(init, S0) ->
last_version(init, S0 ++ [modified]);
last_modified(Other, S) ->
last_version(Other, S).

last_version(init, [Tab, Attrs, Attr]) ->
case lists:member(Attr, Attrs) of
false ->
error_logger:format("Cannot merge table ~p."
"Missing ~p attribute~n", [Tab, Attr]),
stop;
true ->
io:fwrite("Starting merge of ~p (~p)~n", [Tab, Attrs]),
{ok, {Tab, pos(Attr, Tab, Attrs)}}
end;
last_version(done, _) ->
ok;
last_version(Objs, {T, P} = S) when is_list(Objs) ->
Actions = lists:map(fun(Obj) ->
last_version_entry(Obj, T, P)
end, Objs),
{ok, Actions, same, S}.


last_version_entry(Obj, T, P) ->
io:fwrite("last_version_entry(~p)~n", [Obj]),
case Obj of
{A, []} -> {write, A};
{[], B} -> {write, B};
{[A], [B]} ->
ModA = element(P, A),
ModB = element(P, B),
io:fwrite("ModA = ~p, ModB = ~p~n", [ModA, ModB]),
if ModA < ModB ->
{write, B};
ModA > ModB ->
{write, A};
ModA == ModB ->
if A =/= B ->
mnesia:abort({undecided,T,A,B});
true ->
{write, A}
end
end
end.



pos(A, T, L) ->
pos(A, T, L, 2). % record tag is the 1st element in the tuple

pos(H, _, [H|_], P) ->
P;
pos(H, Tab, [_|T], P) ->
pos(H, Tab, T, P+1);
pos(A, Tab, [], _) ->
mnesia:abort({missing_attribute, Tab, A}).

0 comments on commit 649ad0b

Please sign in to comment.