Add PULSE control over sequencer handing out duplicate page numbers
This commit is contained in:
parent
21a3fd6d07
commit
58ced8d14c
2 changed files with 67 additions and 24 deletions
|
@ -23,6 +23,10 @@
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-export([start_link/1, stop/1, get/2]).
|
-export([start_link/1, stop/1, get/2]).
|
||||||
|
-ifdef(TEST).
|
||||||
|
-export([start_link/2]).
|
||||||
|
-compile(export_all).
|
||||||
|
-endif.
|
||||||
|
|
||||||
-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]).
|
||||||
|
@ -37,8 +41,10 @@
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
start_link(FLUs) ->
|
start_link(FLUs) ->
|
||||||
%% gen_server:start_link({local, ?SERVER}, ?MODULE, {FLUs}, []).
|
start_link(FLUs, standard).
|
||||||
gen_server:start_link(?MODULE, {FLUs}, []).
|
|
||||||
|
start_link(FLUs, SeqType) ->
|
||||||
|
gen_server:start_link(?MODULE, {FLUs, SeqType}, []).
|
||||||
|
|
||||||
stop(Pid) ->
|
stop(Pid) ->
|
||||||
gen_server:call(Pid, stop, infinity).
|
gen_server:call(Pid, stop, infinity).
|
||||||
|
@ -48,12 +54,26 @@ get(Pid, NumPages) ->
|
||||||
|
|
||||||
%%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%%
|
%%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%% %%%%
|
||||||
|
|
||||||
init({FLUs}) ->
|
init({FLUs, TypeOrSeed}) ->
|
||||||
MLP = get_max_logical_page(FLUs),
|
MLP = get_max_logical_page(FLUs),
|
||||||
{ok, MLP + 1}.
|
if TypeOrSeed == standard ->
|
||||||
|
{ok, MLP + 1};
|
||||||
|
true ->
|
||||||
|
{Seed, BadPercent, MaxDifference} = TypeOrSeed,
|
||||||
|
random:seed(Seed),
|
||||||
|
{ok, {MLP+1, BadPercent, MaxDifference}}
|
||||||
|
end.
|
||||||
|
|
||||||
handle_call({get, NumPages}, _From, MLP) ->
|
handle_call({get, NumPages}, _From, MLP) when is_integer(MLP) ->
|
||||||
{reply, MLP, MLP + NumPages};
|
{reply, MLP, MLP + NumPages};
|
||||||
|
handle_call({get, NumPages}, _From, {MLP, BadPercent, MaxDifference}) ->
|
||||||
|
Fudge = case random:uniform(100) of
|
||||||
|
N when N < BadPercent ->
|
||||||
|
random:uniform(MaxDifference * 2) - MaxDifference;
|
||||||
|
_ ->
|
||||||
|
0
|
||||||
|
end,
|
||||||
|
{reply, erlang:max(1, MLP + Fudge), {MLP + NumPages, BadPercent, MaxDifference}};
|
||||||
handle_call(stop, _From, MLP) ->
|
handle_call(stop, _From, MLP) ->
|
||||||
{stop, normal, ok, MLP};
|
{stop, normal, ok, MLP};
|
||||||
handle_call(_Request, _From, MLP) ->
|
handle_call(_Request, _From, MLP) ->
|
||||||
|
|
|
@ -65,11 +65,22 @@ initial_state() ->
|
||||||
gen_page(PageSize) ->
|
gen_page(PageSize) ->
|
||||||
binary(PageSize).
|
binary(PageSize).
|
||||||
|
|
||||||
|
gen_seed() ->
|
||||||
|
noshrink({choose(1, 20000), choose(1, 20000), choose(1, 20000)}).
|
||||||
|
|
||||||
|
gen_sequencer_percent() ->
|
||||||
|
frequency([{10, choose(1,100)},
|
||||||
|
{5, choose(90,100)}]).
|
||||||
|
|
||||||
|
gen_sequencer() ->
|
||||||
|
frequency([{100, standard},
|
||||||
|
{50, {gen_seed(), gen_sequencer_percent(), choose(1, 2)}}]).
|
||||||
|
|
||||||
command(#state{run=Run} = S) ->
|
command(#state{run=Run} = S) ->
|
||||||
?LET({NumChains, ChainLen, PageSize},
|
?LET({NumChains, ChainLen, PageSize},
|
||||||
{parameter(num_chains), parameter(chain_len), parameter(page_size)},
|
{parameter(num_chains), parameter(chain_len), parameter(page_size)},
|
||||||
frequency(
|
frequency(
|
||||||
[{10, {call, ?MODULE, setup, [NumChains, ChainLen, PageSize]}}
|
[{10, {call, ?MODULE, setup, [NumChains, ChainLen, PageSize, gen_sequencer()]}}
|
||||||
|| not S#state.is_setup] ++
|
|| not S#state.is_setup] ++
|
||||||
[{10, {call, ?MODULE, append, [Run, gen_page(PageSize)]}}
|
[{10, {call, ?MODULE, append, [Run, gen_page(PageSize)]}}
|
||||||
|| S#state.is_setup] ++
|
|| S#state.is_setup] ++
|
||||||
|
@ -83,7 +94,7 @@ precondition(S, {call, _, _, _}) ->
|
||||||
|
|
||||||
%% Next state transformation, S is the current state and V is the result of the
|
%% Next state transformation, S is the current state and V is the result of the
|
||||||
%% command.
|
%% command.
|
||||||
next_state(S, Res, {call, _, setup, [NumChains, ChainLen, PageSize]}) ->
|
next_state(S, Res, {call, _, setup, [NumChains, ChainLen, PageSize, _SeqType]}) ->
|
||||||
S#state{is_setup=true,
|
S#state{is_setup=true,
|
||||||
num_chains=NumChains,
|
num_chains=NumChains,
|
||||||
chain_len=ChainLen,
|
chain_len=ChainLen,
|
||||||
|
@ -184,18 +195,6 @@ prop_pulse_test_() ->
|
||||||
end}.
|
end}.
|
||||||
|
|
||||||
|
|
||||||
%% If you want to see PULSE causing crazy scheduling, then
|
|
||||||
%% use this code instead of the usual stuff.
|
|
||||||
%% check_trace(Trace, Cmds, _Seed) ->
|
|
||||||
%% Results = [X || {_TS, {result, _Pid, X}} <- Trace],
|
|
||||||
%% {CmdsSeq, CmdsPars} = Cmds,
|
|
||||||
%% NaiveCmds = CmdsSeq ++ lists:flatten(CmdsPars),
|
|
||||||
%% NaiveCommands = [{Sym, Args} || {set,_,{call,_,Sym,Args}} <- NaiveCmds],
|
|
||||||
%% NaiveAppends = [X || {append, _} = X <- NaiveCommands],
|
|
||||||
%% conjunction(
|
|
||||||
%% [{identity, equals(NaiveAppends, NaiveAppends)},
|
|
||||||
%% {bogus_order_check_do_not_use_me, equals(Results, lists:usort(Results))}]).
|
|
||||||
|
|
||||||
%% Example Trace (raw event info, from the ?LOG macro)
|
%% Example Trace (raw event info, from the ?LOG macro)
|
||||||
%%
|
%%
|
||||||
%% [{32014,{call,<0.467.0>,{append,<<"O">>}}},
|
%% [{32014,{call,<0.467.0>,{append,<<"O">>}}},
|
||||||
|
@ -237,16 +236,27 @@ check_trace(Trace, _Cmds, _Seed) ->
|
||||||
fun({call, Pid, {append, _Pg}}, {result, Pid, Res}) ->
|
fun({call, Pid, {append, _Pg}}, {result, Pid, Res}) ->
|
||||||
[AppendResultFilter(Res)] end,
|
[AppendResultFilter(Res)] end,
|
||||||
Events),
|
Events),
|
||||||
|
{_, infinity, AppendLPNs} = lists:last(eqc_temporal:all_future(AppendResults)),
|
||||||
|
|
||||||
%% Desired properties
|
%% Desired properties
|
||||||
AllCallsFinish = eqc_temporal:is_false(eqc_temporal:all_future(Calls)),
|
AllCallsFinish = eqc_temporal:is_false(eqc_temporal:all_future(Calls)),
|
||||||
NoAppendLPNDups = lists:sort(AppendResults) == lists:usort(AppendResults),
|
NoAppendLPNDups = lists:sort(AppendLPNs) == lists:usort(AppendLPNs),
|
||||||
|
|
||||||
|
?WHENFAIL(begin
|
||||||
|
?QC_FMT("*AppendLPNs: ~p\n", [range_ify(AppendLPNs)])
|
||||||
|
end,
|
||||||
conjunction(
|
conjunction(
|
||||||
[
|
[
|
||||||
{all_calls_finish, AllCallsFinish},
|
{all_calls_finish, AllCallsFinish},
|
||||||
{no_append_duplicates, NoAppendLPNDups}
|
{no_append_duplicates, NoAppendLPNDups},
|
||||||
]).
|
%% If you want to see PULSE causing crazy scheduling, then
|
||||||
|
%% change one of the "true orelse" -> "false orelse" below.
|
||||||
|
{bogus_no_gaps,
|
||||||
|
true orelse
|
||||||
|
(AppendLPNs == [] orelse length(range_ify(AppendLPNs)) == 1)},
|
||||||
|
{bogus_exactly_1_to_N,
|
||||||
|
true orelse (AppendLPNs == lists:seq(1, length(AppendLPNs)))}
|
||||||
|
])).
|
||||||
|
|
||||||
%% Presenting command data statistics in a nicer way
|
%% Presenting command data statistics in a nicer way
|
||||||
command_data({set, _, {call, _, Fun, _}}, {_S, _V}) ->
|
command_data({set, _, {call, _, Fun, _}}, {_S, _V}) ->
|
||||||
|
@ -308,15 +318,28 @@ make_chains(ChainLen, [H|T], SmallAcc, BigAcc) ->
|
||||||
make_chains(ChainLen, T, [H|SmallAcc], BigAcc)
|
make_chains(ChainLen, T, [H|SmallAcc], BigAcc)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
setup(NumChains, ChainLen, PageSize) ->
|
setup(NumChains, ChainLen, PageSize, SeqType) ->
|
||||||
N = NumChains * ChainLen,
|
N = NumChains * ChainLen,
|
||||||
FLUs = corfurl_test:setup_basic_flus(N, PageSize, 50000),
|
FLUs = corfurl_test:setup_basic_flus(N, PageSize, 50000),
|
||||||
{ok, Seq} = corfurl_sequencer:start_link(FLUs),
|
{ok, Seq} = corfurl_sequencer:start_link(FLUs, SeqType),
|
||||||
Chains = make_chains(ChainLen, FLUs),
|
Chains = make_chains(ChainLen, FLUs),
|
||||||
%% io:format(user, "Cs = ~p\n", [Chains]),
|
%% io:format(user, "Cs = ~p\n", [Chains]),
|
||||||
Proj = corfurl:new_simple_projection(1, 1, 50000, Chains),
|
Proj = corfurl:new_simple_projection(1, 1, 50000, Chains),
|
||||||
#run{seq=Seq, proj=Proj, flus=FLUs}.
|
#run{seq=Seq, proj=Proj, flus=FLUs}.
|
||||||
|
|
||||||
|
range_ify([]) ->
|
||||||
|
[];
|
||||||
|
range_ify(L) ->
|
||||||
|
[H|T] = lists:sort(L),
|
||||||
|
range_ify(H, H+1, T).
|
||||||
|
|
||||||
|
range_ify(Beginning, Next, [Next|T]) ->
|
||||||
|
range_ify(Beginning, Next+1, T);
|
||||||
|
range_ify(Beginning, Next, [Else|T]) ->
|
||||||
|
[{Beginning, to, Next-1}|range_ify(Else, Else+1, T)];
|
||||||
|
range_ify(Beginning, Next, []) ->
|
||||||
|
[{Beginning, to, Next-1}].
|
||||||
|
|
||||||
-define(LOG(Tag, MkCall),
|
-define(LOG(Tag, MkCall),
|
||||||
event_logger:event({call, self(), Tag}),
|
event_logger:event({call, self(), Tag}),
|
||||||
LOG__Result = MkCall,
|
LOG__Result = MkCall,
|
||||||
|
|
Loading…
Reference in a new issue