Add machi_partition_simulator.erl + refactor to use it
This commit is contained in:
parent
4d3a9ed757
commit
a94374cc8c
2 changed files with 181 additions and 92 deletions
|
@ -34,7 +34,7 @@
|
||||||
-define(Dw(X), io:format(user, "~s ~w\n", [??X, X])).
|
-define(Dw(X), io:format(user, "~s ~w\n", [??X, X])).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/6, stop/1, ping/1,
|
-export([start_link/3, stop/1, ping/1,
|
||||||
calculate_projection_internal_old/1]).
|
calculate_projection_internal_old/1]).
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
|
@ -47,8 +47,7 @@
|
||||||
test_calc_proposed_projection/1,
|
test_calc_proposed_projection/1,
|
||||||
test_write_proposed_projection/1,
|
test_write_proposed_projection/1,
|
||||||
test_read_latest_public_projection/2,
|
test_read_latest_public_projection/2,
|
||||||
test_react_to_env/1,
|
test_react_to_env/1]).
|
||||||
test_reset_thresholds/3]).
|
|
||||||
|
|
||||||
-ifdef(EQC).
|
-ifdef(EQC).
|
||||||
-include_lib("eqc/include/eqc.hrl").
|
-include_lib("eqc/include/eqc.hrl").
|
||||||
|
@ -61,12 +60,8 @@
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
-endif. %TEST
|
-endif. %TEST
|
||||||
|
|
||||||
start_link(MyName, All_list, Seed,
|
start_link(MyName, All_list, MyFLUPid) ->
|
||||||
OldThreshold, NoPartitionThreshold,
|
gen_server:start_link(?MODULE, {MyName, All_list, MyFLUPid}, []).
|
||||||
MyFLUPid) ->
|
|
||||||
gen_server:start_link(?MODULE, {MyName, All_list, Seed,
|
|
||||||
OldThreshold, NoPartitionThreshold,
|
|
||||||
MyFLUPid}, []).
|
|
||||||
|
|
||||||
stop(Pid) ->
|
stop(Pid) ->
|
||||||
gen_server:call(Pid, {stop}, infinity).
|
gen_server:call(Pid, {stop}, infinity).
|
||||||
|
@ -102,18 +97,16 @@ test_read_latest_public_projection(Pid, ReadRepairP) ->
|
||||||
test_react_to_env(Pid) ->
|
test_react_to_env(Pid) ->
|
||||||
gen_server:call(Pid, {test_react_to_env}, infinity).
|
gen_server:call(Pid, {test_react_to_env}, infinity).
|
||||||
|
|
||||||
test_reset_thresholds(Pid, OldThreshold, NoPartitionThreshold) ->
|
|
||||||
gen_server:call(Pid, {test_reset_thresholds, OldThreshold, NoPartitionThreshold}, infinity).
|
|
||||||
|
|
||||||
-endif. % TEST
|
-endif. % TEST
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
init({MyName, All_list, Seed, OldThreshold, NoPartitionThreshold, MyFLUPid}) ->
|
init({MyName, All_list, MyFLUPid}) ->
|
||||||
RunEnv = [{seed, Seed},
|
RunEnv = [%% {seed, Seed},
|
||||||
|
{seed, now()},
|
||||||
{network_partitions, []},
|
{network_partitions, []},
|
||||||
{old_threshold, OldThreshold},
|
%% {old_threshold, OldThreshold},
|
||||||
{no_partition_threshold, NoPartitionThreshold},
|
%% {no_partition_threshold, NoPartitionThreshold},
|
||||||
{up_nodes, not_init_yet}],
|
{up_nodes, not_init_yet}],
|
||||||
BestProj = make_initial_projection(MyName, All_list, All_list,
|
BestProj = make_initial_projection(MyName, All_list, All_list,
|
||||||
[], [{author_proc, init_best}]),
|
[], [{author_proc, init_best}]),
|
||||||
|
@ -170,13 +163,6 @@ handle_call({test_read_latest_public_projection, ReadRepairP}, _From, S) ->
|
||||||
handle_call({test_react_to_env}, _From, S) ->
|
handle_call({test_react_to_env}, _From, S) ->
|
||||||
{TODOtodo, S2} = do_react_to_env(S),
|
{TODOtodo, S2} = do_react_to_env(S),
|
||||||
{reply, TODOtodo, S2};
|
{reply, TODOtodo, S2};
|
||||||
handle_call({test_reset_thresholds, OldThreshold, NoPartitionThreshold}, _From,
|
|
||||||
#ch_mgr{runenv=RunEnv} = S) ->
|
|
||||||
RunEnv2 = replace(RunEnv, [{old_threshold, OldThreshold},
|
|
||||||
{no_partition_threshold, NoPartitionThreshold}]),
|
|
||||||
?D({cc,RunEnv2}),
|
|
||||||
|
|
||||||
{reply, ok, S#ch_mgr{runenv=RunEnv2}};
|
|
||||||
handle_call(_Call, _From, S) ->
|
handle_call(_Call, _From, S) ->
|
||||||
{reply, whaaaaaaaaaa, S}.
|
{reply, whaaaaaaaaaa, S}.
|
||||||
|
|
||||||
|
@ -398,7 +384,7 @@ calc_projection(#ch_mgr{proj=LastProj, runenv=RunEnv} = S, RelativeToServer,
|
||||||
%% NoPartitionThreshold: If the network partition changes, what are the odds
|
%% NoPartitionThreshold: If the network partition changes, what are the odds
|
||||||
%% that there are no partitions at all?
|
%% that there are no partitions at all?
|
||||||
|
|
||||||
calc_projection(OldThreshold, NoPartitionThreshold, LastProj,
|
calc_projection(_OldThreshold, _NoPartitionThreshold, LastProj,
|
||||||
RelativeToServer, Dbg, #ch_mgr{name=MyName,runenv=RunEnv1}=S) ->
|
RelativeToServer, Dbg, #ch_mgr{name=MyName,runenv=RunEnv1}=S) ->
|
||||||
#projection{epoch_number=OldEpochNum,
|
#projection{epoch_number=OldEpochNum,
|
||||||
all_members=All_list,
|
all_members=All_list,
|
||||||
|
@ -407,7 +393,7 @@ calc_projection(OldThreshold, NoPartitionThreshold, LastProj,
|
||||||
} = LastProj,
|
} = LastProj,
|
||||||
LastUp = lists:usort(OldUPI_list ++ OldRepairing_list),
|
LastUp = lists:usort(OldUPI_list ++ OldRepairing_list),
|
||||||
AllMembers = (S#ch_mgr.proj)#projection.all_members,
|
AllMembers = (S#ch_mgr.proj)#projection.all_members,
|
||||||
{Up, _, RunEnv2} = calc_up_nodes(MyName, OldThreshold, NoPartitionThreshold,
|
{Up, _, RunEnv2} = calc_up_nodes(MyName, %OldThreshold, NoPartitionThreshold,
|
||||||
AllMembers, RunEnv1),
|
AllMembers, RunEnv1),
|
||||||
|
|
||||||
NewUp = Up -- LastUp,
|
NewUp = Up -- LastUp,
|
||||||
|
@ -462,44 +448,24 @@ calc_projection(OldThreshold, NoPartitionThreshold, LastProj,
|
||||||
{P, S#ch_mgr{runenv=RunEnv3}}.
|
{P, S#ch_mgr{runenv=RunEnv3}}.
|
||||||
|
|
||||||
calc_up_nodes(#ch_mgr{name=MyName, proj=Proj, runenv=RunEnv1}=S) ->
|
calc_up_nodes(#ch_mgr{name=MyName, proj=Proj, runenv=RunEnv1}=S) ->
|
||||||
OldThreshold = proplists:get_value(old_threshold, RunEnv1),
|
|
||||||
NoPartitionThreshold = proplists:get_value(no_partition_threshold, RunEnv1),
|
|
||||||
AllMembers = Proj#projection.all_members,
|
AllMembers = Proj#projection.all_members,
|
||||||
{UpNodes, Partitions, RunEnv2} =
|
{UpNodes, Partitions, RunEnv2} =
|
||||||
calc_up_nodes(MyName, OldThreshold, NoPartitionThreshold,
|
calc_up_nodes(MyName, AllMembers, RunEnv1),
|
||||||
AllMembers, RunEnv1),
|
|
||||||
{UpNodes, Partitions, S#ch_mgr{runenv=RunEnv2}}.
|
{UpNodes, Partitions, S#ch_mgr{runenv=RunEnv2}}.
|
||||||
|
|
||||||
calc_up_nodes(MyName, OldThreshold, NoPartitionThreshold,
|
calc_up_nodes(MyName, AllMembers, RunEnv1) ->
|
||||||
AllMembers, RunEnv1) ->
|
%% Seed1 = proplists:get_value(seed, RunEnv1),
|
||||||
Seed1 = proplists:get_value(seed, RunEnv1),
|
Partitions2 = machi_partition_simulator:get(AllMembers),
|
||||||
Partitions1 = proplists:get_value(network_partitions, RunEnv1),
|
|
||||||
{Seed2, Partitions2} =
|
|
||||||
calc_network_partitions(AllMembers, Seed1, Partitions1,
|
|
||||||
OldThreshold, NoPartitionThreshold),
|
|
||||||
UpNodes = lists:sort(
|
UpNodes = lists:sort(
|
||||||
[Node || Node <- AllMembers,
|
[Node || Node <- AllMembers,
|
||||||
not lists:member({MyName, Node}, Partitions2),
|
not lists:member({MyName, Node}, Partitions2),
|
||||||
not lists:member({Node, MyName}, Partitions2)]),
|
not lists:member({Node, MyName}, Partitions2)]),
|
||||||
RunEnv2 = replace(RunEnv1,
|
RunEnv2 = replace(RunEnv1,
|
||||||
[{seed, Seed2}, {network_partitions, Partitions2},
|
[%% {seed, Seed2},
|
||||||
|
{network_partitions, Partitions2},
|
||||||
{up_nodes, UpNodes}]),
|
{up_nodes, UpNodes}]),
|
||||||
{UpNodes, Partitions2, RunEnv2}.
|
{UpNodes, Partitions2, RunEnv2}.
|
||||||
|
|
||||||
calc_network_partitions(Nodes, Seed1, OldPartition,
|
|
||||||
OldThreshold, NoPartitionThreshold) ->
|
|
||||||
{Cutoff2, Seed2} = random:uniform_s(100, Seed1),
|
|
||||||
if Cutoff2 < OldThreshold ->
|
|
||||||
{Seed2, OldPartition};
|
|
||||||
true ->
|
|
||||||
{Cutoff3, Seed3} = random:uniform_s(100, Seed1),
|
|
||||||
if Cutoff3 < NoPartitionThreshold ->
|
|
||||||
{Seed3, []};
|
|
||||||
true ->
|
|
||||||
make_network_partition_locations(Nodes, Seed3)
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
replace(PropList, Items) ->
|
replace(PropList, Items) ->
|
||||||
lists:foldl(fun({Key, Val}, Ps) ->
|
lists:foldl(fun({Key, Val}, Ps) ->
|
||||||
lists:keyreplace(Key, 1, Ps, {Key,Val})
|
lists:keyreplace(Key, 1, Ps, {Key,Val})
|
||||||
|
@ -947,34 +913,6 @@ find_common_prefix(_, _) ->
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
make_network_partition_locations(Nodes, Seed1) ->
|
|
||||||
%% TODO: To simplify debugging a bit, I'm switching to partitions that are
|
|
||||||
%% bi-directional only.
|
|
||||||
Num = length(Nodes),
|
|
||||||
{Seed2, WeightsNodes} = lists:foldl(
|
|
||||||
fun(Node, {Seeda, Acc}) ->
|
|
||||||
{Cutoff, Seedb} =
|
|
||||||
random:uniform_s(100, Seeda),
|
|
||||||
{Seedb, [{Cutoff, Node}|Acc]}
|
|
||||||
end, {Seed1, []}, Nodes),
|
|
||||||
IslandSep = 100 div Num,
|
|
||||||
Islands = [
|
|
||||||
[Nd || {Weight, Nd} <- WeightsNodes,
|
|
||||||
(Max - IslandSep) =< Weight, Weight < Max]
|
|
||||||
|| Max <- lists:seq(IslandSep + 1, 101, IslandSep)],
|
|
||||||
{Seed2, lists:usort(make_islands(Islands))}.
|
|
||||||
|
|
||||||
make_islands([]) ->
|
|
||||||
[];
|
|
||||||
make_islands([Island|Rest]) ->
|
|
||||||
[{X,Y} || X <- Island,
|
|
||||||
Y <- lists:append(Rest), X /= Y]
|
|
||||||
++
|
|
||||||
[{Y,X} || X <- Island,
|
|
||||||
Y <- lists:append(Rest), X /= Y]
|
|
||||||
++
|
|
||||||
make_islands(Rest).
|
|
||||||
|
|
||||||
perhaps_call_t(S, Partitions, FLU, DoIt) ->
|
perhaps_call_t(S, Partitions, FLU, DoIt) ->
|
||||||
try
|
try
|
||||||
perhaps_call(S, Partitions, FLU, DoIt)
|
perhaps_call(S, Partitions, FLU, DoIt)
|
||||||
|
@ -1006,8 +944,9 @@ perhaps_call(#ch_mgr{name=MyName, myflu=MyFLU}, Partitions, FLU, DoIt) ->
|
||||||
-define(MGR, machi_chain_manager1).
|
-define(MGR, machi_chain_manager1).
|
||||||
|
|
||||||
smoke0_test() ->
|
smoke0_test() ->
|
||||||
|
machi_partition_simulator:start_link({1,2,3}, 50, 50),
|
||||||
{ok, FLUa} = machi_flu0:start_link(a),
|
{ok, FLUa} = machi_flu0:start_link(a),
|
||||||
{ok, M0} = ?MGR:start_link(a, [a,b,c], {1,2,3}, 50, 50, a),
|
{ok, M0} = ?MGR:start_link(a, [a,b,c], a),
|
||||||
try
|
try
|
||||||
pong = ping(M0),
|
pong = ping(M0),
|
||||||
|
|
||||||
|
@ -1020,16 +959,17 @@ smoke0_test() ->
|
||||||
end || _ <- lists:seq(1,5)]
|
end || _ <- lists:seq(1,5)]
|
||||||
after
|
after
|
||||||
ok = ?MGR:stop(M0),
|
ok = ?MGR:stop(M0),
|
||||||
ok = machi_flu0:stop(FLUa)
|
ok = machi_flu0:stop(FLUa),
|
||||||
|
ok = machi_partition_simulator:stop()
|
||||||
end.
|
end.
|
||||||
|
|
||||||
smoke1_test() ->
|
smoke1_test() ->
|
||||||
|
machi_partition_simulator:start_link({1,2,3}, 100, 0),
|
||||||
{ok, FLUa} = machi_flu0:start_link(a),
|
{ok, FLUa} = machi_flu0:start_link(a),
|
||||||
{ok, FLUb} = machi_flu0:start_link(b),
|
{ok, FLUb} = machi_flu0:start_link(b),
|
||||||
{ok, FLUc} = machi_flu0:start_link(c),
|
{ok, FLUc} = machi_flu0:start_link(c),
|
||||||
I_represent = I_am = a,
|
I_represent = I_am = a,
|
||||||
%% {ok, M0} = ?MGR:start_link(I_represent, [a,b,c], {1,2,3}, 50, 50, I_am),
|
{ok, M0} = ?MGR:start_link(I_represent, [a,b,c], I_am),
|
||||||
{ok, M0} = ?MGR:start_link(I_represent, [a,b,c], {1,2,3}, 0, 100, I_am),
|
|
||||||
try
|
try
|
||||||
%% ?D(x),
|
%% ?D(x),
|
||||||
{ok, _P1} = test_calc_projection(M0, false),
|
{ok, _P1} = test_calc_projection(M0, false),
|
||||||
|
@ -1046,15 +986,17 @@ smoke1_test() ->
|
||||||
ok = ?MGR:stop(M0),
|
ok = ?MGR:stop(M0),
|
||||||
ok = machi_flu0:stop(FLUa),
|
ok = machi_flu0:stop(FLUa),
|
||||||
ok = machi_flu0:stop(FLUb),
|
ok = machi_flu0:stop(FLUb),
|
||||||
ok = machi_flu0:stop(FLUc)
|
ok = machi_flu0:stop(FLUc),
|
||||||
|
ok = machi_partition_simulator:stop()
|
||||||
end.
|
end.
|
||||||
|
|
||||||
nonunanimous_setup_and_fix_test() ->
|
nonunanimous_setup_and_fix_test() ->
|
||||||
|
machi_partition_simulator:start_link({1,2,3}, 100, 0),
|
||||||
{ok, FLUa} = machi_flu0:start_link(a),
|
{ok, FLUa} = machi_flu0:start_link(a),
|
||||||
{ok, FLUb} = machi_flu0:start_link(b),
|
{ok, FLUb} = machi_flu0:start_link(b),
|
||||||
I_represent = I_am = a,
|
I_represent = I_am = a,
|
||||||
{ok, Ma} = ?MGR:start_link(I_represent, [a,b], {1,2,3}, 0, 100, I_am),
|
{ok, Ma} = ?MGR:start_link(I_represent, [a,b], I_am),
|
||||||
{ok, Mb} = ?MGR:start_link(b, [a,b], {4,5,6}, 0, 100, b),
|
{ok, Mb} = ?MGR:start_link(b, [a,b], b),
|
||||||
try
|
try
|
||||||
{ok, P1} = test_calc_projection(Ma, false),
|
{ok, P1} = test_calc_projection(Ma, false),
|
||||||
|
|
||||||
|
@ -1096,17 +1038,20 @@ nonunanimous_setup_and_fix_test() ->
|
||||||
ok = ?MGR:stop(Ma),
|
ok = ?MGR:stop(Ma),
|
||||||
ok = ?MGR:stop(Mb),
|
ok = ?MGR:stop(Mb),
|
||||||
ok = machi_flu0:stop(FLUa),
|
ok = machi_flu0:stop(FLUa),
|
||||||
ok = machi_flu0:stop(FLUb)
|
ok = machi_flu0:stop(FLUb),
|
||||||
|
ok = machi_partition_simulator:stop()
|
||||||
end.
|
end.
|
||||||
|
|
||||||
zoof_test() ->
|
zoof_test() ->
|
||||||
|
machi_partition_simulator:start_link({1,2,3}, 50, 50),
|
||||||
|
|
||||||
{ok, FLUa} = machi_flu0:start_link(a),
|
{ok, FLUa} = machi_flu0:start_link(a),
|
||||||
{ok, FLUb} = machi_flu0:start_link(b),
|
{ok, FLUb} = machi_flu0:start_link(b),
|
||||||
{ok, FLUc} = machi_flu0:start_link(c),
|
{ok, FLUc} = machi_flu0:start_link(c),
|
||||||
I_represent = I_am = a,
|
I_represent = I_am = a,
|
||||||
{ok, Ma} = ?MGR:start_link(I_represent, [a,b,c], {1,2,3}, 50, 50, I_am),
|
{ok, Ma} = ?MGR:start_link(I_represent, [a,b,c], I_am),
|
||||||
{ok, Mb} = ?MGR:start_link(b, [a,b,c], {4,5,6}, 50, 50, b),
|
{ok, Mb} = ?MGR:start_link(b, [a,b,c], b),
|
||||||
{ok, Mc} = ?MGR:start_link(c, [a,b,c], {7,8,9}, 50, 50, c),
|
{ok, Mc} = ?MGR:start_link(c, [a,b,c], c),
|
||||||
?D(x),
|
?D(x),
|
||||||
try
|
try
|
||||||
{ok, P1} = test_calc_projection(Ma, false),
|
{ok, P1} = test_calc_projection(Ma, false),
|
||||||
|
@ -1142,7 +1087,7 @@ zoof_test() ->
|
||||||
end,
|
end,
|
||||||
|
|
||||||
DoIt(),
|
DoIt(),
|
||||||
[test_reset_thresholds(M, 100, 0) || M <- [Ma, Mb, Mc]],
|
machi_partition_simulator:reset_thresholds(100, 0),
|
||||||
DoIt(),
|
DoIt(),
|
||||||
DoIt(),
|
DoIt(),
|
||||||
|
|
||||||
|
@ -1191,7 +1136,8 @@ zoof_test() ->
|
||||||
ok = ?MGR:stop(Ma),
|
ok = ?MGR:stop(Ma),
|
||||||
ok = ?MGR:stop(Mb),
|
ok = ?MGR:stop(Mb),
|
||||||
ok = machi_flu0:stop(FLUa),
|
ok = machi_flu0:stop(FLUa),
|
||||||
ok = machi_flu0:stop(FLUb)
|
ok = machi_flu0:stop(FLUb),
|
||||||
|
ok = machi_partition_simulator:stop()
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|
143
prototype/poc-machi/test/machi_partition_simulator.erl
Normal file
143
prototype/poc-machi/test/machi_partition_simulator.erl
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
%% -------------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% Machi: a small village of replicated files
|
||||||
|
%%
|
||||||
|
%% Copyright (c) 2014 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
|
||||||
|
%% except in compliance with the License. You may obtain
|
||||||
|
%% a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing,
|
||||||
|
%% software distributed under the License is distributed on an
|
||||||
|
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
%% KIND, either express or implied. See the License for the
|
||||||
|
%% specific language governing permissions and limitations
|
||||||
|
%% under the License.
|
||||||
|
%%
|
||||||
|
%% -------------------------------------------------------------------
|
||||||
|
-module(machi_partition_simulator).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
|
||||||
|
-ifdef(EQC).
|
||||||
|
-include_lib("eqc/include/eqc.hrl").
|
||||||
|
-endif.
|
||||||
|
-ifdef(PULSE).
|
||||||
|
-compile({parse_transform, pulse_instrument}).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-export([start_link/3, stop/0,
|
||||||
|
get/1, reset_thresholds/2]).
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
|
-define(TAB, ?MODULE).
|
||||||
|
|
||||||
|
-record(state, {
|
||||||
|
seed,
|
||||||
|
old_partitions,
|
||||||
|
old_threshold,
|
||||||
|
no_partition_threshold
|
||||||
|
}).
|
||||||
|
|
||||||
|
start_link(Seed, OldThreshold, NoPartitionThreshold) ->
|
||||||
|
gen_server:start_link({local, ?MODULE}, ?MODULE,
|
||||||
|
{Seed, OldThreshold, NoPartitionThreshold}, []).
|
||||||
|
|
||||||
|
stop() ->
|
||||||
|
gen_server:call(?MODULE, {stop}, infinity).
|
||||||
|
|
||||||
|
get(Nodes) ->
|
||||||
|
gen_server:call(?MODULE, {get, Nodes}, infinity).
|
||||||
|
|
||||||
|
reset_thresholds(OldThreshold, NoPartitionThreshold) ->
|
||||||
|
gen_server:call(?MODULE, {reset_thresholds, OldThreshold, NoPartitionThreshold}, infinity).
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
init({Seed, OldThreshold, NoPartitionThreshold}) ->
|
||||||
|
{ok, #state{seed=Seed,
|
||||||
|
old_partitions=[],
|
||||||
|
old_threshold=OldThreshold,
|
||||||
|
no_partition_threshold=NoPartitionThreshold}}.
|
||||||
|
|
||||||
|
handle_call({get, Nodes}, _From, S) ->
|
||||||
|
{Seed2, Partitions2} =
|
||||||
|
calc_network_partitions(Nodes,
|
||||||
|
S#state.seed,
|
||||||
|
S#state.old_partitions,
|
||||||
|
S#state.old_threshold,
|
||||||
|
S#state.no_partition_threshold),
|
||||||
|
{reply, Partitions2, S#state{seed=Seed2}};
|
||||||
|
handle_call({reset_thresholds, OldThreshold, NoPartitionThreshold}, _From, S) ->
|
||||||
|
{reply, ok, S#state{old_threshold=OldThreshold,
|
||||||
|
no_partition_threshold=NoPartitionThreshold}};
|
||||||
|
handle_call({stop}, _From, S) ->
|
||||||
|
{stop, normal, ok, S}.
|
||||||
|
|
||||||
|
handle_cast(_Cast, S) ->
|
||||||
|
{noreply, S}.
|
||||||
|
|
||||||
|
handle_info(_Info, S) ->
|
||||||
|
{noreply, S}.
|
||||||
|
|
||||||
|
terminate(_Reason, _S) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, S, _Extra) ->
|
||||||
|
{ok, S}.
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
calc_network_partitions(Nodes, Seed1, OldPartition,
|
||||||
|
OldThreshold, NoPartitionThreshold) ->
|
||||||
|
{Cutoff2, Seed2} = random:uniform_s(100, Seed1),
|
||||||
|
if Cutoff2 < OldThreshold ->
|
||||||
|
{Seed2, OldPartition};
|
||||||
|
true ->
|
||||||
|
{Cutoff3, Seed3} = random:uniform_s(100, Seed1),
|
||||||
|
if Cutoff3 < NoPartitionThreshold ->
|
||||||
|
{Seed3, []};
|
||||||
|
true ->
|
||||||
|
make_network_partition_locations(Nodes, Seed3)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
make_network_partition_locations(Nodes, Seed1) ->
|
||||||
|
%% TODO: To simplify debugging a bit, I'm switching to partitions that are
|
||||||
|
%% bi-directional only.
|
||||||
|
Num = length(Nodes),
|
||||||
|
{Seed2, WeightsNodes} = lists:foldl(
|
||||||
|
fun(Node, {Seeda, Acc}) ->
|
||||||
|
{Cutoff, Seedb} =
|
||||||
|
random:uniform_s(100, Seeda),
|
||||||
|
{Seedb, [{Cutoff, Node}|Acc]}
|
||||||
|
end, {Seed1, []}, Nodes),
|
||||||
|
IslandSep = 100 div Num,
|
||||||
|
Islands = [
|
||||||
|
[Nd || {Weight, Nd} <- WeightsNodes,
|
||||||
|
(Max - IslandSep) =< Weight, Weight < Max]
|
||||||
|
|| Max <- lists:seq(IslandSep + 1, 101, IslandSep)],
|
||||||
|
{Seed2, lists:usort(make_islands(Islands))}.
|
||||||
|
|
||||||
|
make_islands([]) ->
|
||||||
|
[];
|
||||||
|
make_islands([Island|Rest]) ->
|
||||||
|
[{X,Y} || X <- Island,
|
||||||
|
Y <- lists:append(Rest), X /= Y]
|
||||||
|
++
|
||||||
|
[{Y,X} || X <- Island,
|
||||||
|
Y <- lists:append(Rest), X /= Y]
|
||||||
|
++
|
||||||
|
make_islands(Rest).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-endif. % TEST
|
Loading…
Reference in a new issue