Merge pull request #9 from jlouis/qc-sync-fold-range
Qc sync fold range
This commit is contained in:
commit
739fd03715
2 changed files with 39 additions and 12 deletions
|
@ -13,6 +13,7 @@
|
|||
open/1, close/1,
|
||||
put/3,
|
||||
sync_range/3,
|
||||
sync_fold_range/5,
|
||||
stop/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
|
@ -53,6 +54,9 @@ put(N, K, V) ->
|
|||
sync_range(T, LK, HK) ->
|
||||
call({sync_range, T, LK, HK}).
|
||||
|
||||
sync_fold_range(T, Fun, Acc0, LK, HK) ->
|
||||
call({sync_fold_range, T, Fun, Acc0, LK, HK}).
|
||||
|
||||
stop() ->
|
||||
call(stop).
|
||||
|
||||
|
@ -82,6 +86,12 @@ handle_call({sync_range, Name, LoKey, HiKey}, _From,
|
|||
{ok, Ref} = lsm_btree:sync_range(Tree, LoKey, HiKey),
|
||||
Result = sync_range_gather(Ref),
|
||||
{reply, Result, State};
|
||||
handle_call({sync_fold_range, Name, Fun, Acc0, LoKey, HiKey},
|
||||
_From,
|
||||
#state { btrees = D } = State) ->
|
||||
Tree = dict:fetch(Name, D),
|
||||
Result = lsm_btree:sync_fold_range(Tree, Fun, Acc0, LoKey, HiKey),
|
||||
{reply, Result, State};
|
||||
handle_call({put, N, K, V}, _, #state { btrees = D} = State) ->
|
||||
Tree = dict:fetch(N, D),
|
||||
case lsm_btree:put(Tree, K, V) of
|
||||
|
@ -100,7 +110,6 @@ handle_call({lookup, N, K}, _, #state { btrees = D} = State) ->
|
|||
{reply, Reply, State};
|
||||
handle_call(stop, _, #state{ btrees = D } = State ) ->
|
||||
[ lsm_btree:close(Tree) || {_,Tree} <- dict:to_list(D) ],
|
||||
cleanup_trees(State),
|
||||
{stop, normal, ok, State};
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
|
@ -119,16 +128,6 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
{ok, State}.
|
||||
|
||||
%%%===================================================================
|
||||
|
||||
%% @todo directory cleanup
|
||||
cleanup_trees(#state { btrees = BTs }) ->
|
||||
dict:fold(fun(_Name, Tree, ok) ->
|
||||
lsm_btree:close(Tree)
|
||||
end,
|
||||
ok,
|
||||
BTs).
|
||||
|
||||
|
||||
sync_range_gather(Ref) ->
|
||||
sync_range_gather(Ref, []).
|
||||
|
||||
|
|
|
@ -92,6 +92,9 @@ g_non_existing_key(Name, Open) ->
|
|||
not dict:is_key(Key, D)
|
||||
end).
|
||||
|
||||
g_fold_operation() ->
|
||||
oneof([{fun (K, V, Acc) -> [{K, V} | Acc] end, []}]).
|
||||
|
||||
btree_name(I) ->
|
||||
"Btree_" ++ integer_to_list(I).
|
||||
|
||||
|
@ -122,11 +125,15 @@ command(#state { open = Open, closed = Closed } = S) ->
|
|||
|| open_dicts(S), open_dicts_with_keys(S)]
|
||||
++ [ {500, {call, ?SERVER, delete_exist, cmd_delete_args(S)}}
|
||||
|| open_dicts(S), open_dicts_with_keys(S)]
|
||||
++ [ {250, {call, ?SERVER, sync_range, cmd_sync_range_args(S)}}
|
||||
++ [ {125, {call, ?SERVER, sync_fold_range, cmd_sync_fold_range_args(S)}}
|
||||
|| open_dicts(S), open_dicts_with_keys(S)]
|
||||
++ [ {125, {call, ?SERVER, sync_range, cmd_sync_range_args(S)}}
|
||||
|| open_dicts(S), open_dicts_with_keys(S)]
|
||||
).
|
||||
|
||||
%% Precondition (abstract)
|
||||
precondition(S, {call, ?SERVER, sync_fold_range, [_Tree, _F, _A0, _K1, _K2]}) ->
|
||||
open_dicts(S) andalso open_dicts_with_keys(S);
|
||||
precondition(S, {call, ?SERVER, sync_range, [_Tree, _K1, _K2]}) ->
|
||||
open_dicts(S) andalso open_dicts_with_keys(S);
|
||||
precondition(S, {call, ?SERVER, delete_exist, [_Name, _K]}) ->
|
||||
|
@ -145,6 +152,8 @@ precondition(#state { open = Open, closed = Closed },
|
|||
(dict:is_key(Name, Open)) and (not dict:is_key(Name, Closed)).
|
||||
|
||||
%% Next state manipulation (abstract / concrete)
|
||||
next_state(S, _Res, {call, ?SERVER, sync_fold_range, [_Tree, _F, _A0, _K1, _K2]}) ->
|
||||
S;
|
||||
next_state(S, _Res, {call, ?SERVER, sync_range, [_Tree, _K1, _K2]}) ->
|
||||
S;
|
||||
next_state(S, _Res, {call, ?SERVER, lookup_fail, [_Name, _Key]}) ->
|
||||
|
@ -178,6 +187,11 @@ next_state(#state { open = Open, closed=Closed} = S, _Res,
|
|||
open = dict:erase(Name, Open) }.
|
||||
|
||||
%% Postcondition check (concrete)
|
||||
postcondition(#state { open = Open},
|
||||
{call, ?SERVER, sync_fold_range, [Tree, F, A0, K1, K2]}, Result) ->
|
||||
#tree { elements = TDict } = dict:fetch(Tree, Open),
|
||||
lists:sort(dict_range_query(TDict, F, A0, K1, K2))
|
||||
== lists:sort(Result);
|
||||
postcondition(#state { open = Open},
|
||||
{call, ?SERVER, sync_range, [Tree, K1, K2]}, {ok, Result}) ->
|
||||
#tree { elements = TDict } = dict:fetch(Tree, Open),
|
||||
|
@ -199,6 +213,7 @@ postcondition(_S, {call, ?SERVER, open, [_Name]}, ok) ->
|
|||
postcondition(_S, {call, ?SERVER, close, [_Name]}, ok) ->
|
||||
true;
|
||||
postcondition(_, _, _) ->
|
||||
error_logger:error_report([{not_matching_any_postcondition}]),
|
||||
false.
|
||||
|
||||
|
||||
|
@ -335,6 +350,11 @@ cmd_sync_range_args(#state { open = Open }) ->
|
|||
g_existing_key(Tree, Open)},
|
||||
[Tree, K1, K2])).
|
||||
|
||||
cmd_sync_fold_range_args(State) ->
|
||||
?LET([Tree, K1, K2], cmd_sync_range_args(State),
|
||||
?LET({F, Acc0}, g_fold_operation(),
|
||||
[Tree, F, Acc0, K1, K2])).
|
||||
|
||||
%% Context management
|
||||
%% ----------------------------------------------------------------------
|
||||
cleanup_test_trees(#state { open = Open, closed = Closed }) ->
|
||||
|
@ -366,6 +386,14 @@ open_dicts(#state { open = Open}) ->
|
|||
closed_dicts(#state { closed = Closed}) ->
|
||||
dict:size(Closed) > 0.
|
||||
|
||||
dict_range_query(Dict, Fun, Acc0, LowKey, HighKey) ->
|
||||
KVs = dict_range_query(Dict, LowKey, HighKey),
|
||||
lists:foldl(fun({K, V}, Acc) ->
|
||||
Fun(K, V, Acc)
|
||||
end,
|
||||
Acc0,
|
||||
KVs).
|
||||
|
||||
dict_range_query(Dict, LowKey, HighKey) ->
|
||||
[{K, V} || {K, V} <- dict:to_list(Dict),
|
||||
K >= LowKey,
|
||||
|
|
Loading…
Reference in a new issue