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,
|
open/1, close/1,
|
||||||
put/3,
|
put/3,
|
||||||
sync_range/3,
|
sync_range/3,
|
||||||
|
sync_fold_range/5,
|
||||||
stop/0]).
|
stop/0]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
|
@ -53,6 +54,9 @@ put(N, K, V) ->
|
||||||
sync_range(T, LK, HK) ->
|
sync_range(T, LK, HK) ->
|
||||||
call({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() ->
|
stop() ->
|
||||||
call(stop).
|
call(stop).
|
||||||
|
|
||||||
|
@ -82,6 +86,12 @@ handle_call({sync_range, Name, LoKey, HiKey}, _From,
|
||||||
{ok, Ref} = lsm_btree:sync_range(Tree, LoKey, HiKey),
|
{ok, Ref} = lsm_btree:sync_range(Tree, LoKey, HiKey),
|
||||||
Result = sync_range_gather(Ref),
|
Result = sync_range_gather(Ref),
|
||||||
{reply, Result, State};
|
{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) ->
|
handle_call({put, N, K, V}, _, #state { btrees = D} = State) ->
|
||||||
Tree = dict:fetch(N, D),
|
Tree = dict:fetch(N, D),
|
||||||
case lsm_btree:put(Tree, K, V) of
|
case lsm_btree:put(Tree, K, V) of
|
||||||
|
@ -100,7 +110,6 @@ handle_call({lookup, N, K}, _, #state { btrees = D} = State) ->
|
||||||
{reply, Reply, State};
|
{reply, Reply, State};
|
||||||
handle_call(stop, _, #state{ btrees = D } = State ) ->
|
handle_call(stop, _, #state{ btrees = D } = State ) ->
|
||||||
[ lsm_btree:close(Tree) || {_,Tree} <- dict:to_list(D) ],
|
[ lsm_btree:close(Tree) || {_,Tree} <- dict:to_list(D) ],
|
||||||
cleanup_trees(State),
|
|
||||||
{stop, normal, ok, State};
|
{stop, normal, ok, State};
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
Reply = ok,
|
Reply = ok,
|
||||||
|
@ -119,16 +128,6 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{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) ->
|
||||||
sync_range_gather(Ref, []).
|
sync_range_gather(Ref, []).
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,9 @@ g_non_existing_key(Name, Open) ->
|
||||||
not dict:is_key(Key, D)
|
not dict:is_key(Key, D)
|
||||||
end).
|
end).
|
||||||
|
|
||||||
|
g_fold_operation() ->
|
||||||
|
oneof([{fun (K, V, Acc) -> [{K, V} | Acc] end, []}]).
|
||||||
|
|
||||||
btree_name(I) ->
|
btree_name(I) ->
|
||||||
"Btree_" ++ integer_to_list(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)]
|
|| open_dicts(S), open_dicts_with_keys(S)]
|
||||||
++ [ {500, {call, ?SERVER, delete_exist, cmd_delete_args(S)}}
|
++ [ {500, {call, ?SERVER, delete_exist, cmd_delete_args(S)}}
|
||||||
|| open_dicts(S), open_dicts_with_keys(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)]
|
|| open_dicts(S), open_dicts_with_keys(S)]
|
||||||
).
|
).
|
||||||
|
|
||||||
%% Precondition (abstract)
|
%% 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]}) ->
|
precondition(S, {call, ?SERVER, sync_range, [_Tree, _K1, _K2]}) ->
|
||||||
open_dicts(S) andalso open_dicts_with_keys(S);
|
open_dicts(S) andalso open_dicts_with_keys(S);
|
||||||
precondition(S, {call, ?SERVER, delete_exist, [_Name, _K]}) ->
|
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)).
|
(dict:is_key(Name, Open)) and (not dict:is_key(Name, Closed)).
|
||||||
|
|
||||||
%% Next state manipulation (abstract / concrete)
|
%% 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]}) ->
|
next_state(S, _Res, {call, ?SERVER, sync_range, [_Tree, _K1, _K2]}) ->
|
||||||
S;
|
S;
|
||||||
next_state(S, _Res, {call, ?SERVER, lookup_fail, [_Name, _Key]}) ->
|
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) }.
|
open = dict:erase(Name, Open) }.
|
||||||
|
|
||||||
%% Postcondition check (concrete)
|
%% 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},
|
postcondition(#state { open = Open},
|
||||||
{call, ?SERVER, sync_range, [Tree, K1, K2]}, {ok, Result}) ->
|
{call, ?SERVER, sync_range, [Tree, K1, K2]}, {ok, Result}) ->
|
||||||
#tree { elements = TDict } = dict:fetch(Tree, Open),
|
#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) ->
|
postcondition(_S, {call, ?SERVER, close, [_Name]}, ok) ->
|
||||||
true;
|
true;
|
||||||
postcondition(_, _, _) ->
|
postcondition(_, _, _) ->
|
||||||
|
error_logger:error_report([{not_matching_any_postcondition}]),
|
||||||
false.
|
false.
|
||||||
|
|
||||||
|
|
||||||
|
@ -335,6 +350,11 @@ cmd_sync_range_args(#state { open = Open }) ->
|
||||||
g_existing_key(Tree, Open)},
|
g_existing_key(Tree, Open)},
|
||||||
[Tree, K1, K2])).
|
[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
|
%% Context management
|
||||||
%% ----------------------------------------------------------------------
|
%% ----------------------------------------------------------------------
|
||||||
cleanup_test_trees(#state { open = Open, closed = Closed }) ->
|
cleanup_test_trees(#state { open = Open, closed = Closed }) ->
|
||||||
|
@ -366,6 +386,14 @@ open_dicts(#state { open = Open}) ->
|
||||||
closed_dicts(#state { closed = Closed}) ->
|
closed_dicts(#state { closed = Closed}) ->
|
||||||
dict:size(Closed) > 0.
|
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) ->
|
dict_range_query(Dict, LowKey, HighKey) ->
|
||||||
[{K, V} || {K, V} <- dict:to_list(Dict),
|
[{K, V} || {K, V} <- dict:to_list(Dict),
|
||||||
K >= LowKey,
|
K >= LowKey,
|
||||||
|
|
Loading…
Reference in a new issue