Add PULSE control over sequencer handing out duplicate page numbers

This commit is contained in:
Scott Lystig Fritchie 2014-02-17 22:04:51 +09:00
parent 21a3fd6d07
commit 58ced8d14c
2 changed files with 67 additions and 24 deletions

View file

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

View file

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