WIP: Refactoring and prototyping goop, broken test
This commit is contained in:
parent
8820a71152
commit
9cf77f4406
3 changed files with 203 additions and 54 deletions
|
@ -2,7 +2,7 @@
|
|||
%%
|
||||
%% Machi: a small village of replicated files
|
||||
%%
|
||||
%% Copyright (c) 2014 Basho Technologies, Inc. All Rights Reserved.
|
||||
%% Copyright (c) 2014-2015 Basho Technologies, Inc. All Rights Reserved.
|
||||
%%
|
||||
%% This file is provided to you under the Apache License,
|
||||
%% Version 2.0 (the "License"); you may not use this file
|
||||
|
@ -262,6 +262,9 @@ catch _Err:_What ->
|
|||
exit({line, ?LINE, _Err, _What})
|
||||
end,
|
||||
io:format(user, "Yay!\n", []),
|
||||
ReportXXX = machi_chain_manager1_test:unanimous_report(Namez),
|
||||
true = machi_chain_manager1_test:all_reports_are_disjoint(ReportXXX),
|
||||
io:format(user, "\nLast Reports: ~p\n", [lists:nthtail(length(ReportXXX)-8,ReportXXX)]),
|
||||
timer:sleep(1250),
|
||||
ok
|
||||
end || {Partition, Count} <- PartitionCounts
|
||||
|
@ -287,6 +290,7 @@ io:format(user, "Yay!\n", []),
|
|||
%% members appear in only one unique chain, i.e., the sets of
|
||||
%% unique chains are disjoint.
|
||||
true = machi_chain_manager1_test:all_reports_are_disjoint(Report),
|
||||
io:format(user, "\nLast Reports: ~p\n", [lists:nthtail(length(Report)-8,Report)]),
|
||||
|
||||
%% For each chain transition experienced by a particular FLU,
|
||||
%% confirm that each state transition is OK.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
%%
|
||||
%% Machi: a small village of replicated files
|
||||
%%
|
||||
%% Copyright (c) 2014 Basho Technologies, Inc. All Rights Reserved.
|
||||
%% Copyright (c) 2014-2015 Basho Technologies, Inc. All Rights Reserved.
|
||||
%%
|
||||
%% This file is provided to you under the Apache License,
|
||||
%% Version 2.0 (the "License"); you may not use this file
|
||||
|
@ -82,7 +82,8 @@ command(S) ->
|
|||
{ 1, {call, ?MODULE, change_partitions,
|
||||
[gen_old_threshold(), gen_no_partition_threshold()]}},
|
||||
{50, {call, ?MODULE, do_ticks,
|
||||
[choose(5, 200), S#state.pids,
|
||||
%% [choose(5, 200), S#state.pids,
|
||||
[choose(5, 10), S#state.pids,
|
||||
gen_old_threshold(), gen_no_partition_threshold()]}}
|
||||
]).
|
||||
|
||||
|
@ -225,32 +226,31 @@ dump_state() ->
|
|||
%% [io_lib:format("~p ~p ~p: ~w\n", [FLUName, Type, P#projection_v1.epoch_number, machi_projection:make_summary(P)]) || P <- Ps]
|
||||
%% end || {FLUName, Proxy} <- orddict:to_list(ProxiesDict),
|
||||
%% Type <- [public] ],
|
||||
|
||||
UniquePrivateEs =
|
||||
lists:usort(lists:flatten(
|
||||
[element(2,?FLU_PC:list_all_projections(Proxy,private)) ||
|
||||
{_FLUName, Proxy} <- orddict:to_list(ProxiesDict)])),
|
||||
P_lists0 = [{FLUName, Type,
|
||||
element(2,?FLU_PC:get_all_projections(Proxy, Type))} ||
|
||||
{FLUName, Proxy} <- orddict:to_list(ProxiesDict),
|
||||
Type <- [public,private]],
|
||||
P_lists = [{FLUName, Type, P} || {FLUName, Type, Ps} <- P_lists0,
|
||||
P <- Ps],
|
||||
AllDict = lists:foldl(fun({FLU, Type, P}, D) ->
|
||||
K = {FLU, Type, P#projection_v1.epoch_number},
|
||||
dict:store(K, P, D)
|
||||
end, dict:new(), lists:flatten(P_lists)),
|
||||
DumbFinderBackward =
|
||||
fun(FLUName) ->
|
||||
fun(E, error_unwritten) ->
|
||||
case dict:find({FLUName, private, E}, AllDict) of
|
||||
{ok, T} -> T;
|
||||
error -> error_unwritten
|
||||
end;
|
||||
(_E, Acc) ->
|
||||
Acc
|
||||
end
|
||||
end,
|
||||
%% P_lists0 = [{FLUName, Type,
|
||||
%% element(2,?FLU_PC:get_all_projections(Proxy, Type))} ||
|
||||
%% {FLUName, Proxy} <- orddict:to_list(ProxiesDict),
|
||||
%% Type <- [public,private]],
|
||||
%% P_lists = [{FLUName, Type, P} || {FLUName, Type, Ps} <- P_lists0,
|
||||
%% P <- Ps],
|
||||
%% AllDict = lists:foldl(fun({FLU, Type, P}, D) ->
|
||||
%% K = {FLU, Type, P#projection_v1.epoch_number},
|
||||
%% dict:store(K, P, D)
|
||||
%% end, dict:new(), lists:flatten(P_lists)),
|
||||
%% DumbFinderBackward =
|
||||
%% fun(FLUName) ->
|
||||
%% fun(E, error_unwritten) ->
|
||||
%% case dict:find({FLUName, private, E}, AllDict) of
|
||||
%% {ok, T} -> T;
|
||||
%% error -> error_unwritten
|
||||
%% end;
|
||||
%% (_E, Acc) ->
|
||||
%% Acc
|
||||
%% end
|
||||
%% end,
|
||||
%% UniquePrivateEs =
|
||||
%% lists:usort(lists:flatten(
|
||||
%% [element(2,?FLU_PC:list_all_projections(Proxy,private)) ||
|
||||
%% {_FLUName, Proxy} <- orddict:to_list(ProxiesDict)])),
|
||||
%% Diag2 = [[
|
||||
%% io_lib:format("~p private: ~w\n",
|
||||
%% [FLUName,
|
||||
|
@ -295,7 +295,6 @@ prop_pulse() ->
|
|||
LastTriggerTicks = {set,{var,99999997},
|
||||
{call, ?MODULE, do_ticks, [123, undefined, no, no]}},
|
||||
Cmds1 = lists:duplicate(2, LastTriggerTicks),
|
||||
%% Cmds1 = lists:duplicate(length(all_list())*2, LastTriggerTicks),
|
||||
Cmds = Cmds0 ++
|
||||
Stabilize1 ++
|
||||
Cmds1 ++
|
||||
|
|
|
@ -45,36 +45,47 @@
|
|||
-include_lib("eunit/include/eunit.hrl").
|
||||
-compile(export_all).
|
||||
|
||||
%% @doc Create a summary report of all of the *private* projections of
|
||||
%% each of the FLUs in the chain, and create a summary for each
|
||||
%% epoch number.
|
||||
%%
|
||||
%% Report format: list({EpochNumber::non_neg_integer(), Report::rpt()})
|
||||
%% rpt(): {ok_disjoint | bummer_NOT_DISJOINT, agree_list()}
|
||||
%% agree_list(): list(agree())
|
||||
%% agree(): {agreed_membership, UPIandRepairing()} |
|
||||
%% {not_agreed, ......TODO LEFT OFF HERE
|
||||
%%
|
||||
%% Example:
|
||||
%% [{1,{ok_disjoint,[{agreed_membership,{[a],[b,c]}}]}},
|
||||
%% {3,{ok_disjoint,[{agreed_membership,{[a],[b,c]}}]}},
|
||||
%% {8,
|
||||
%% {ok_disjoint,[{not_agreed,{[a],[b,c]},
|
||||
%% [{b,not_in_this_epoch},
|
||||
%% <<65,159,66,113,232,15,156,244,197,
|
||||
%% 210,39,82,229,84,192,19,27,45,161,38>>]}]}},
|
||||
%% {10,{ok_disjoint,[{agreed_membership,{[c],[]}}]}},
|
||||
%% ...]
|
||||
%% TODO LEFT OFF HERE
|
||||
|
||||
unanimous_report(Namez) ->
|
||||
UniquePrivateEs =
|
||||
lists:usort(lists:flatten(
|
||||
[element(2, ?FLU_PC:list_all_projections(FLU, private)) ||
|
||||
{_FLUName, FLU} <- Namez])),
|
||||
[unanimous_report(Epoch, Namez) || Epoch <- UniquePrivateEs,
|
||||
Epoch /= 0].
|
||||
[{Epoch, unanimous_report(Epoch, Namez)} || Epoch <- UniquePrivateEs,
|
||||
Epoch /= 0].
|
||||
|
||||
unanimous_report(Epoch, Namez) ->
|
||||
Projs = [{FLUName,
|
||||
case ?FLU_PC:read_projection(FLU, private, Epoch) of
|
||||
{ok, T} ->
|
||||
machi_chain_manager1:inner_projection_or_self(T);
|
||||
_Else ->
|
||||
{FLUName, not_in_this_epoch}
|
||||
end} || {FLUName, FLU} <- Namez],
|
||||
FLU_Projs = [{FLUName,
|
||||
case ?FLU_PC:read_projection(FLU, private, Epoch) of
|
||||
{ok, T} ->
|
||||
machi_chain_manager1:inner_projection_or_self(T);
|
||||
_Else ->
|
||||
not_in_this_epoch
|
||||
end} || {FLUName, FLU} <- Namez],
|
||||
unanimous_report2(Epoch, FLU_Projs).
|
||||
|
||||
unanimous_report2(Epoch, FLU_Projs) ->
|
||||
ProjsSumms = [{FLU, if is_tuple(P) ->
|
||||
Summ = machi_projection:make_summary(P),
|
||||
lists:flatten(io_lib:format("~w", [Summ]));
|
||||
is_atom(P) ->
|
||||
P
|
||||
end} || {FLU, P} <- FLU_Projs],
|
||||
UPI_R_Sums = [{Proj#projection_v1.upi, Proj#projection_v1.repairing,
|
||||
Proj#projection_v1.epoch_csum} ||
|
||||
{_FLUname, Proj} <- Projs,
|
||||
{_FLUname, Proj} <- FLU_Projs,
|
||||
is_record(Proj, projection_v1)],
|
||||
UniqueUPIs = lists:usort([UPI || {UPI, _Repairing, _CSum} <- UPI_R_Sums]),
|
||||
Res =
|
||||
|
@ -86,7 +97,7 @@ unanimous_report(Epoch, Namez) ->
|
|||
%% that all FLUs are in agreement.
|
||||
{UPI, Repairing, _CSum} =
|
||||
lists:keyfind(UPI, 1, UPI_R_Sums),
|
||||
Tmp = [{FLU, case proplists:get_value(FLU, Projs) of
|
||||
Tmp = [{FLU, case proplists:get_value(FLU, FLU_Projs) of
|
||||
P when is_record(P, projection_v1) ->
|
||||
P#projection_v1.epoch_csum;
|
||||
Else ->
|
||||
|
@ -95,11 +106,11 @@ unanimous_report(Epoch, Namez) ->
|
|||
case lists:usort([CSum || {_FLU, CSum} <- Tmp]) of
|
||||
[_] ->
|
||||
{agreed_membership, {UPI, Repairing}};
|
||||
Else2 ->
|
||||
{not_agreed, {UPI, Repairing}, Else2}
|
||||
_Else2 ->
|
||||
{not_agreed, ProjsSumms}
|
||||
end;
|
||||
_Else ->
|
||||
{not_agreed, {undefined, undefined}, Projs}
|
||||
{not_agreed, [checksum_disagreement|ProjsSumms]}
|
||||
end
|
||||
end || UPI <- UniqueUPIs],
|
||||
AgreedResUPI_Rs = [UPI++Repairing ||
|
||||
|
@ -111,7 +122,7 @@ unanimous_report(Epoch, Namez) ->
|
|||
false ->
|
||||
bummer_NOT_DISJOINT
|
||||
end,
|
||||
{Epoch, {Tag, Res}}.
|
||||
{Tag, Res}.
|
||||
|
||||
all_reports_are_disjoint(Report) ->
|
||||
[] == [X || {_Epoch, Tuple}=X <- Report,
|
||||
|
@ -130,6 +141,110 @@ chain_to_projection(MyName, Epoch, UPI_list, Repairing_list, All_list) ->
|
|||
All_list -- (UPI_list ++ Repairing_list),
|
||||
UPI_list, Repairing_list, [{artificial_by, ?MODULE}]).
|
||||
|
||||
simple_chain_state_transition_is_sane(UPI1, Repair1, UPI2) ->
|
||||
{KeepsDels, Orders} = mk(UPI1, Repair1, UPI2),
|
||||
NumKeeps = length([x || keep <- KeepsDels]),
|
||||
NumOrders = length(Orders),
|
||||
false == lists:member(error, Orders)
|
||||
andalso Orders == lists:sort(Orders)
|
||||
andalso length(UPI2) == NumKeeps + NumOrders.
|
||||
|
||||
mk(UPI1, Repair1, UPI2) ->
|
||||
mk(UPI1, Repair1, UPI2, []).
|
||||
|
||||
mk([X|UPI1], Repair1, [X|UPI2], Acc) ->
|
||||
mk(UPI1, Repair1, UPI2, [keep|Acc]);
|
||||
mk([X|UPI1], Repair1, UPI2, Acc) ->
|
||||
mk(UPI1, Repair1, UPI2 -- [X], [del|Acc]);
|
||||
mk([], [], [], Acc) ->
|
||||
{lists:reverse(Acc), []};
|
||||
mk([], Repair1, UPI2, Acc) ->
|
||||
{lists:reverse(Acc), mk_order(UPI2, Repair1)}.
|
||||
|
||||
mk_order(UPI2, Repair1) ->
|
||||
R1 = length(Repair1),
|
||||
Repair1_order_d = orddict:from_list(lists:zip(Repair1, lists:seq(1, R1))),
|
||||
UPI2_order = [case orddict:find(X, Repair1_order_d) of
|
||||
{ok, Idx} -> Idx;
|
||||
error -> error
|
||||
end || X <- UPI2],
|
||||
UPI2_order.
|
||||
|
||||
perms([]) -> [[]];
|
||||
perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].
|
||||
|
||||
combinations(L) ->
|
||||
lists:usort(perms(L) ++ lists:append([ combinations(L -- [X]) || X <- L])).
|
||||
|
||||
ordered_combinations(Master) ->
|
||||
[L || L <- combinations(Master), is_ordered(L, Master)].
|
||||
|
||||
is_ordered(L, Reference) ->
|
||||
L_order = mk_order(L, Reference),
|
||||
lists:all(fun(X) -> is_integer(X) end, L_order) andalso
|
||||
L_order == lists:sort(L_order).
|
||||
|
||||
mk_order_is_sane_test_() ->
|
||||
{timeout, 300, fun() -> mk_order_is_sane_test2() end}.
|
||||
|
||||
mk_order_is_sane_test2() ->
|
||||
%% All: A list of all FLUS for a particular test
|
||||
%% UPI1: some combination of All that represents UPI1
|
||||
%% Repair1: Some combination of (All -- UP1) that represents Repairing1
|
||||
%% ... then we test check_sane_func_with_upi_and_repair() with all
|
||||
%% possible UPI1 and Repair1.
|
||||
[true = check_sane_func_with_upi_and_repair(UPI1, Repair1) ||
|
||||
%% The five elements below runs on my MacBook Pro in about 4.8 seconds
|
||||
%% All <- [ [a], [a,b], [a,b,c], [a,b,c,d], [a,b,c,d,e] ],
|
||||
All <- [ [a], [a,b], [a,b,c], [a,b,c,d] ],
|
||||
UPI1 <- combinations(All),
|
||||
Repair1 <- combinations(All -- UPI1)].
|
||||
|
||||
%% Given a UPI1 and Repair1 list, we calculate all possible good UPI2
|
||||
%% lists. For all good {UPI1, Repair1} -> UPI2 transitions, then the
|
||||
%% simple_chain_state_transition_is_sane() function must be true. For
|
||||
%% all other UPI2 transitions, simple_chain_state_transition_is_sane()
|
||||
%% must be false.
|
||||
%%
|
||||
%% We include adding an extra possible participant, 'bogus', to the
|
||||
%% list of all possible UPI2 transitions, just to demonstrate that
|
||||
%% adding an extra element/participant/thingie is never sane.
|
||||
|
||||
check_sane_func_with_upi_and_repair([], []) ->
|
||||
true;
|
||||
check_sane_func_with_upi_and_repair(UPI1, Repair1) ->
|
||||
Good_UPI2s = [ X ++ Y || X <- ordered_combinations(UPI1),
|
||||
Y <- ordered_combinations(Repair1)],
|
||||
All_UPI2s = combinations(lists:usort(UPI1 ++ Repair1) ++ [bogus]),
|
||||
|
||||
[true = simple_chain_state_transition_is_sane(UPI1, Repair1, UPI2) ||
|
||||
UPI2 <- Good_UPI2s],
|
||||
[false = simple_chain_state_transition_is_sane(UPI1, Repair1, UPI2) ||
|
||||
UPI2 <- (All_UPI2s -- Good_UPI2s)],
|
||||
|
||||
All = UPI1 ++ Repair1,
|
||||
Me = hd(All),
|
||||
MembersDict = orddict:from_list([{X, #p_srvr{name=X}} || X <- All]),
|
||||
[true = begin
|
||||
Down1 = All -- (UPI1 ++ Repair1),
|
||||
Repair2 = [],
|
||||
Down2 = All -- (UPI2 ++ Repair2),
|
||||
P1 = machi_projection:new(1, Me, MembersDict,
|
||||
Down1, UPI1, Repair1, []),
|
||||
P2 = machi_projection:new(2, Me, MembersDict,
|
||||
Down2, UPI2, Repair2, []),
|
||||
QQ = machi_chain_manager1:projection_transition_is_sane(P1, P2, Me,
|
||||
false),
|
||||
if QQ /= true ->
|
||||
io:format(user, "QQ ~p\n", [QQ]);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
QQ
|
||||
end || UPI2 <- Good_UPI2s],
|
||||
|
||||
true.
|
||||
|
||||
-ifndef(PULSE).
|
||||
|
||||
smoke0_test() ->
|
||||
|
@ -250,5 +365,36 @@ nonunanimous_setup_and_fix_test() ->
|
|||
ok = machi_partition_simulator:stop()
|
||||
end.
|
||||
|
||||
foo_TODO_UNFINISHED_KEEP_PERHAPS_unanimous_report_test() ->
|
||||
TcpPort = 63877,
|
||||
FluInfo = [{a,TcpPort+0,"./data.a"}, {b,TcpPort+1,"./data.b"}],
|
||||
P_s = [#p_srvr{name=Name, address="localhost", port=Port} ||
|
||||
{Name,Port,_Dir} <- FluInfo],
|
||||
MembersDict = machi_projection:make_members_dict(P_s),
|
||||
|
||||
E5 = 5,
|
||||
UPI5 = [a,b],
|
||||
Rep5 = [],
|
||||
P5 = machi_projection:new(E5, a, MembersDict, [], UPI5, Rep5, []),
|
||||
{ok_disjoint, [{agreed_membership, {UPI5, Rep5}}]} =
|
||||
unanimous_report2(E5, [{a, P5}, {b, P5}]),
|
||||
{ok_disjoint, [{not_agreed, _}]} =
|
||||
unanimous_report2(E5, [{a, not_in_this_epoch}, {b, P5}]),
|
||||
{ok_disjoint, [{not_agreed, _}]} =
|
||||
unanimous_report2(E5, [{a, P5}, {b, not_in_this_epoch}]),
|
||||
|
||||
P5_other_csum = machi_projection:update_checksum(
|
||||
P5#projection_v1{author_server=b}),
|
||||
{ok_disjoint, [{not_agreed, _}]} =
|
||||
unanimous_report2(E5, [{a, P5}, {b, P5_other_csum}]),
|
||||
|
||||
UPI5_b = [a],
|
||||
Rep5_b = [b],
|
||||
P5_b = machi_projection:new(E5, b, MembersDict, [], UPI5_b, Rep5_b, []),
|
||||
XX =
|
||||
unanimous_report2(E5, [{a, P5}, {b, P5_b}]),
|
||||
|
||||
io:format(user, "\nXX ~p\n", [XX]).
|
||||
|
||||
-endif. % !PULSE
|
||||
-endif. % TEST
|
||||
|
|
Loading…
Reference in a new issue