WIP: Refactoring and prototyping goop, broken test

This commit is contained in:
Scott Lystig Fritchie 2015-07-03 00:59:04 +09:00
parent 8820a71152
commit 9cf77f4406
3 changed files with 203 additions and 54 deletions

View file

@ -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.

View file

@ -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 ++

View file

@ -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