Merge pull request #9 from jlouis/qc-sync-fold-range

Qc sync fold range
This commit is contained in:
Kresten Krab Thorup 2012-01-22 04:57:51 -08:00
commit 739fd03715
2 changed files with 39 additions and 12 deletions

View file

@ -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, []).

View file

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