2012-01-07 16:17:48 +00:00
|
|
|
-module(lsm_btree_tests).
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-04-15 10:40:43 +00:00
|
|
|
-include("include/lsm_btree.hrl").
|
|
|
|
-include("src/lsm_btree.hrl").
|
|
|
|
|
2012-01-04 14:05:31 +00:00
|
|
|
-ifdef(TEST).
|
2012-01-07 14:44:31 +00:00
|
|
|
-ifdef(TRIQ).
|
|
|
|
-include_lib("triq/include/triq.hrl").
|
|
|
|
-include_lib("triq/include/triq_statem.hrl").
|
|
|
|
-else.
|
2012-01-06 21:56:23 +00:00
|
|
|
-include_lib("proper/include/proper.hrl").
|
2012-01-07 14:44:31 +00:00
|
|
|
-endif.
|
2012-01-04 14:05:31 +00:00
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
-endif.
|
|
|
|
|
2012-01-07 14:44:31 +00:00
|
|
|
-ifdef(PROPER).
|
2012-01-06 21:56:23 +00:00
|
|
|
-behaviour(proper_statem).
|
2012-01-07 14:44:31 +00:00
|
|
|
-endif.
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-06 21:56:23 +00:00
|
|
|
-compile(export_all).
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-06 21:56:23 +00:00
|
|
|
-export([command/1, initial_state/0,
|
|
|
|
next_state/3, postcondition/3,
|
|
|
|
precondition/2]).
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-21 14:15:19 +00:00
|
|
|
-record(tree, { elements = dict:new() }).
|
2012-01-06 21:56:23 +00:00
|
|
|
-record(state, { open = dict:new(),
|
|
|
|
closed = dict:new() }).
|
2012-01-07 16:17:48 +00:00
|
|
|
-define(SERVER, lsm_btree_drv).
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-06 21:56:23 +00:00
|
|
|
full_test_() ->
|
|
|
|
{setup,
|
|
|
|
spawn,
|
|
|
|
fun () -> ok end,
|
|
|
|
fun (_) -> ok end,
|
2012-01-07 13:21:38 +00:00
|
|
|
[
|
2012-01-07 00:02:09 +00:00
|
|
|
?_test(test_tree_simple_1()),
|
2012-01-07 11:43:49 +00:00
|
|
|
?_test(test_tree_simple_2()),
|
2012-01-21 20:16:24 +00:00
|
|
|
?_test(test_tree_simple_3()),
|
|
|
|
?_test(test_tree_simple_4()),
|
2012-04-19 17:57:39 +00:00
|
|
|
{timeout, 300, ?_test(test_tree())},
|
2012-01-19 13:25:47 +00:00
|
|
|
{timeout, 120, ?_test(test_qc())}
|
2012-01-07 13:21:38 +00:00
|
|
|
]}.
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-07 14:44:31 +00:00
|
|
|
-ifdef(TRIQ).
|
|
|
|
test_qc() ->
|
|
|
|
[?assertEqual(true, triq:module(?MODULE))].
|
|
|
|
-else.
|
2012-01-07 00:02:09 +00:00
|
|
|
qc_opts() -> [{numtests, 800}].
|
2012-01-07 14:44:31 +00:00
|
|
|
test_qc() ->
|
2012-01-06 21:56:23 +00:00
|
|
|
[?assertEqual([], proper:module(?MODULE, qc_opts()))].
|
2012-01-07 14:44:31 +00:00
|
|
|
-endif.
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Generators
|
|
|
|
%% ----------------------------------------------------------------------
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-07 14:16:57 +00:00
|
|
|
-define(NUM_TREES, 10).
|
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Generate a name for a btree
|
2012-01-06 21:56:23 +00:00
|
|
|
g_btree_name() ->
|
2012-01-07 14:44:31 +00:00
|
|
|
?LET(I, choose(1,?NUM_TREES),
|
2012-01-07 14:16:57 +00:00
|
|
|
btree_name(I)).
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-07 21:58:38 +00:00
|
|
|
%% Generate a key for the Tree
|
|
|
|
g_key() ->
|
|
|
|
binary().
|
|
|
|
|
|
|
|
%% Generate a value for the Tree
|
|
|
|
g_value() ->
|
|
|
|
binary().
|
|
|
|
|
|
|
|
g_fail_key() ->
|
|
|
|
?LET(T, choose(1,999999999999),
|
|
|
|
term_to_binary(T)).
|
|
|
|
|
2012-01-07 22:35:27 +00:00
|
|
|
g_open_tree(Open) ->
|
|
|
|
oneof(dict:fetch_keys(Open)).
|
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Pick a name of a non-empty Btree
|
2012-01-07 22:35:27 +00:00
|
|
|
g_non_empty_btree(Open) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
?LET(TreesWithKeys, dict:filter(fun(_K, #tree { elements = D}) ->
|
|
|
|
dict:size(D) > 0
|
|
|
|
end,
|
|
|
|
Open),
|
2012-01-07 22:35:27 +00:00
|
|
|
oneof(dict:fetch_keys(TreesWithKeys))).
|
2012-01-07 21:58:38 +00:00
|
|
|
|
|
|
|
g_existing_key(Name, Open) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
#tree { elements = Elems } = dict:fetch(Name, Open),
|
|
|
|
oneof(dict:fetch_keys(Elems)).
|
2012-01-07 21:58:38 +00:00
|
|
|
|
|
|
|
g_non_existing_key(Name, Open) ->
|
|
|
|
?SUCHTHAT(Key, g_fail_key(),
|
|
|
|
begin
|
2012-01-21 14:15:19 +00:00
|
|
|
#tree { elements = D } = dict:fetch(Name, Open),
|
2012-01-07 21:58:38 +00:00
|
|
|
not dict:is_key(Key, D)
|
|
|
|
end).
|
2012-01-07 00:02:09 +00:00
|
|
|
|
2012-01-22 11:34:22 +00:00
|
|
|
g_fold_operation() ->
|
|
|
|
oneof([{fun (K, V, Acc) -> [{K, V} | Acc] end, []}]).
|
|
|
|
|
2012-01-07 14:16:57 +00:00
|
|
|
btree_name(I) ->
|
|
|
|
"Btree_" ++ integer_to_list(I).
|
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Statem test
|
|
|
|
%% ----------------------------------------------------------------------
|
|
|
|
initial_state() ->
|
2012-01-07 14:16:57 +00:00
|
|
|
ClosedBTrees = lists:foldl(fun(N, Closed) ->
|
|
|
|
dict:store(btree_name(N),
|
2012-01-21 14:15:19 +00:00
|
|
|
#tree { },
|
2012-01-07 14:16:57 +00:00
|
|
|
Closed)
|
|
|
|
end,
|
|
|
|
dict:new(),
|
|
|
|
lists:seq(1,?NUM_TREES)),
|
|
|
|
#state { closed=ClosedBTrees }.
|
2012-01-07 00:02:09 +00:00
|
|
|
|
|
|
|
|
2012-01-07 14:16:57 +00:00
|
|
|
command(#state { open = Open, closed = Closed } = S) ->
|
2012-01-06 21:56:23 +00:00
|
|
|
frequency(
|
2012-01-21 14:15:19 +00:00
|
|
|
[ {20, {call, ?SERVER, open, [oneof(dict:fetch_keys(Closed))]}}
|
|
|
|
|| closed_dicts(S)]
|
|
|
|
++ [ {20, {call, ?SERVER, close, [oneof(dict:fetch_keys(Open))]}}
|
|
|
|
|| open_dicts(S)]
|
|
|
|
++ [ {2000, {call, ?SERVER, put, cmd_put_args(S)}}
|
|
|
|
|| open_dicts(S)]
|
2012-04-15 14:35:39 +00:00
|
|
|
++ [ {1500, {call, ?SERVER, get_fail, cmd_get_fail_args(S)}}
|
2012-01-21 14:15:19 +00:00
|
|
|
|| open_dicts(S)]
|
2012-04-15 14:35:39 +00:00
|
|
|
++ [ {1500, {call, ?SERVER, get_exist, cmd_get_args(S)}}
|
2012-01-21 14:15:19 +00:00
|
|
|
|| 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)]
|
2012-01-22 11:34:22 +00:00
|
|
|
++ [ {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)}}
|
2012-01-21 20:16:24 +00:00
|
|
|
|| open_dicts(S), open_dicts_with_keys(S)]
|
2012-01-21 14:15:19 +00:00
|
|
|
).
|
2012-01-07 11:43:49 +00:00
|
|
|
|
|
|
|
%% Precondition (abstract)
|
2012-04-15 10:40:43 +00:00
|
|
|
precondition(S, {call, ?SERVER, sync_fold_range, [_Tree, _F, _A0, Range]}) ->
|
|
|
|
is_valid_range(Range) andalso open_dicts(S) andalso open_dicts_with_keys(S);
|
|
|
|
precondition(S, {call, ?SERVER, sync_range, [_Tree, Range]}) ->
|
|
|
|
is_valid_range(Range) andalso open_dicts(S) andalso open_dicts_with_keys(S);
|
2012-01-21 14:15:19 +00:00
|
|
|
precondition(S, {call, ?SERVER, delete_exist, [_Name, _K]}) ->
|
|
|
|
open_dicts(S) andalso open_dicts_with_keys(S);
|
2012-04-15 14:35:39 +00:00
|
|
|
precondition(S, {call, ?SERVER, get_fail, [_Name, _K]}) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
open_dicts(S);
|
2012-04-15 14:35:39 +00:00
|
|
|
precondition(S, {call, ?SERVER, get_exist, [_Name, _K]}) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
open_dicts(S) andalso open_dicts_with_keys(S);
|
2012-01-07 11:43:49 +00:00
|
|
|
precondition(#state { open = Open }, {call, ?SERVER, put, [Name, _K, _V]}) ->
|
2012-01-06 21:56:23 +00:00
|
|
|
dict:is_key(Name, Open);
|
2012-01-21 14:15:19 +00:00
|
|
|
precondition(#state { open = Open, closed = Closed },
|
|
|
|
{call, ?SERVER, open, [Name]}) ->
|
2012-01-07 14:16:57 +00:00
|
|
|
(not (dict:is_key(Name, Open))) and (dict:is_key(Name, Closed));
|
2012-01-21 14:15:19 +00:00
|
|
|
precondition(#state { open = Open, closed = Closed },
|
|
|
|
{call, ?SERVER, close, [Name]}) ->
|
2012-01-07 14:16:57 +00:00
|
|
|
(dict:is_key(Name, Open)) and (not dict:is_key(Name, Closed)).
|
|
|
|
|
2012-04-15 10:40:43 +00:00
|
|
|
is_valid_range(#btree_range{ from_key=FromKey, from_inclusive=FromIncl,
|
|
|
|
to_key=ToKey, to_inclusive=ToIncl,
|
|
|
|
limit=Limit })
|
|
|
|
when
|
|
|
|
(Limit == undefined) orelse (Limit > 0),
|
|
|
|
is_binary(FromKey),
|
|
|
|
(ToKey == undefined) orelse is_binary(ToKey),
|
|
|
|
FromKey =< ToKey,
|
|
|
|
is_boolean(FromIncl),
|
|
|
|
is_boolean(ToIncl)
|
|
|
|
->
|
|
|
|
if (FromKey == ToKey) ->
|
|
|
|
(FromIncl == true) and (ToIncl == true);
|
|
|
|
true ->
|
|
|
|
true
|
|
|
|
end;
|
|
|
|
is_valid_range(_) ->
|
|
|
|
false.
|
|
|
|
|
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Next state manipulation (abstract / concrete)
|
2012-04-15 10:40:43 +00:00
|
|
|
next_state(S, _Res, {call, ?SERVER, sync_fold_range, [_Tree, _F, _A0, _Range]}) ->
|
2012-01-22 11:34:22 +00:00
|
|
|
S;
|
2012-04-15 10:40:43 +00:00
|
|
|
next_state(S, _Res, {call, ?SERVER, sync_range, [_Tree, _Range]}) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
S;
|
2012-04-15 14:35:39 +00:00
|
|
|
next_state(S, _Res, {call, ?SERVER, get_fail, [_Name, _Key]}) ->
|
2012-01-07 21:58:38 +00:00
|
|
|
S;
|
2012-04-15 14:35:39 +00:00
|
|
|
next_state(S, _Res, {call, ?SERVER, get_exist, [_Name, _Key]}) ->
|
2012-01-07 00:02:09 +00:00
|
|
|
S;
|
2012-01-07 11:43:49 +00:00
|
|
|
next_state(#state { open = Open} = S, _Res,
|
|
|
|
{call, ?SERVER, delete_exist, [Name, Key]}) ->
|
|
|
|
S#state { open = dict:update(Name,
|
2012-01-21 14:15:19 +00:00
|
|
|
fun(#tree { elements = Dict}) ->
|
|
|
|
#tree { elements =
|
2012-04-15 14:35:39 +00:00
|
|
|
dict:erase(Key, Dict)}
|
2012-01-07 11:43:49 +00:00
|
|
|
end,
|
|
|
|
Open)};
|
2012-01-06 21:56:23 +00:00
|
|
|
next_state(#state { open = Open} = S, _Res,
|
|
|
|
{call, ?SERVER, put, [Name, Key, Value]}) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
S#state { open = dict:update(
|
|
|
|
Name,
|
|
|
|
fun(#tree { elements = Dict}) ->
|
|
|
|
#tree { elements =
|
|
|
|
dict:store(Key, Value, Dict) }
|
|
|
|
end,
|
|
|
|
Open)};
|
|
|
|
next_state(#state { open = Open, closed=Closed} = S,
|
|
|
|
_Res, {call, ?SERVER, open, [Name]}) ->
|
2012-01-07 14:16:57 +00:00
|
|
|
S#state { open = dict:store(Name, dict:fetch(Name, Closed) , Open),
|
|
|
|
closed = dict:erase(Name, Closed) };
|
2012-01-21 14:15:19 +00:00
|
|
|
next_state(#state { open = Open, closed=Closed} = S, _Res,
|
|
|
|
{call, ?SERVER, close, [Name]}) ->
|
2012-01-07 14:16:57 +00:00
|
|
|
S#state { closed = dict:store(Name, dict:fetch(Name, Open) , Closed),
|
|
|
|
open = dict:erase(Name, Open) }.
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Postcondition check (concrete)
|
2012-01-22 11:34:22 +00:00
|
|
|
postcondition(#state { open = Open},
|
2012-04-15 10:40:43 +00:00
|
|
|
{call, ?SERVER, sync_fold_range, [Tree, F, A0, Range]}, Result) ->
|
2012-01-22 11:34:22 +00:00
|
|
|
#tree { elements = TDict } = dict:fetch(Tree, Open),
|
2012-04-15 10:40:43 +00:00
|
|
|
DictResult = lists:sort(dict_range_query(TDict, F, A0, Range)),
|
|
|
|
CallResult = lists:sort(Result),
|
|
|
|
DictResult == CallResult;
|
2012-01-21 14:15:19 +00:00
|
|
|
postcondition(#state { open = Open},
|
2012-04-15 10:40:43 +00:00
|
|
|
{call, ?SERVER, sync_range, [Tree, Range]}, {ok, Result}) ->
|
2012-01-21 20:16:24 +00:00
|
|
|
#tree { elements = TDict } = dict:fetch(Tree, Open),
|
2012-04-15 10:40:43 +00:00
|
|
|
DictResult = lists:sort(dict_range_query(TDict, Range)),
|
|
|
|
CallResult = lists:sort(Result),
|
|
|
|
DictResult == CallResult;
|
2012-01-07 22:35:27 +00:00
|
|
|
postcondition(_S,
|
2012-04-16 11:31:14 +00:00
|
|
|
{call, ?SERVER, get_fail, [_Name, _Key]}, not_found) ->
|
2012-01-07 21:58:38 +00:00
|
|
|
true;
|
2012-01-07 00:02:09 +00:00
|
|
|
postcondition(#state { open = Open },
|
2012-04-15 14:35:39 +00:00
|
|
|
{call, ?SERVER, get_exist, [Name, Key]}, {ok, Value}) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
#tree { elements = Elems } = dict:fetch(Name, Open),
|
|
|
|
dict:fetch(Key, Elems) == Value;
|
2012-01-07 11:43:49 +00:00
|
|
|
postcondition(_S, {call, ?SERVER, delete_exist, [_Name, _Key]}, ok) ->
|
|
|
|
true;
|
2012-01-06 21:56:23 +00:00
|
|
|
postcondition(_S, {call, ?SERVER, put, [_Name, _Key, _Value]}, ok) ->
|
|
|
|
true;
|
|
|
|
postcondition(_S, {call, ?SERVER, open, [_Name]}, ok) ->
|
|
|
|
true;
|
2012-01-07 14:16:57 +00:00
|
|
|
postcondition(_S, {call, ?SERVER, close, [_Name]}, ok) ->
|
|
|
|
true;
|
2012-01-06 21:56:23 +00:00
|
|
|
postcondition(_, _, _) ->
|
2012-01-22 11:34:22 +00:00
|
|
|
error_logger:error_report([{not_matching_any_postcondition}]),
|
2012-01-06 21:56:23 +00:00
|
|
|
false.
|
2012-01-04 14:05:31 +00:00
|
|
|
|
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Main property. Running a random set of commands is in agreement
|
|
|
|
%% with a dict.
|
2012-01-06 21:56:23 +00:00
|
|
|
prop_dict_agree() ->
|
|
|
|
?FORALL(Cmds, commands(?MODULE),
|
|
|
|
?TRAPEXIT(
|
|
|
|
begin
|
2012-01-07 16:17:48 +00:00
|
|
|
lsm_btree_drv:start_link(),
|
2012-01-06 21:56:23 +00:00
|
|
|
{History,State,Result} = run_commands(?MODULE, Cmds),
|
2012-01-07 16:17:48 +00:00
|
|
|
lsm_btree_drv:stop(),
|
2012-01-07 00:02:09 +00:00
|
|
|
cleanup_test_trees(State),
|
2012-01-06 21:56:23 +00:00
|
|
|
?WHENFAIL(io:format("History: ~w\nState: ~w\nResult: ~w\n",
|
|
|
|
[History,State,Result]),
|
2012-01-07 14:44:31 +00:00
|
|
|
Result =:= ok)
|
2012-01-06 21:56:23 +00:00
|
|
|
end)).
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% UNIT TESTS
|
2012-01-06 21:56:23 +00:00
|
|
|
%% ----------------------------------------------------------------------
|
2012-01-07 00:02:09 +00:00
|
|
|
test_tree_simple_1() ->
|
2012-01-07 16:17:48 +00:00
|
|
|
{ok, Tree} = lsm_btree:open("simple"),
|
|
|
|
ok = lsm_btree:put(Tree, <<>>, <<"data", 77:128>>),
|
2012-04-15 14:35:39 +00:00
|
|
|
{ok, <<"data", 77:128>>} = lsm_btree:get(Tree, <<>>),
|
2012-01-07 16:17:48 +00:00
|
|
|
ok = lsm_btree:close(Tree).
|
2012-01-07 00:02:09 +00:00
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
test_tree_simple_2() ->
|
2012-01-07 16:17:48 +00:00
|
|
|
{ok, Tree} = lsm_btree:open("simple"),
|
|
|
|
ok = lsm_btree:put(Tree, <<"ã">>, <<"µ">>),
|
|
|
|
ok = lsm_btree:delete(Tree, <<"ã">>),
|
|
|
|
ok = lsm_btree:close(Tree).
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-21 20:16:24 +00:00
|
|
|
test_tree_simple_3() ->
|
|
|
|
{ok, Tree} = lsm_btree:open("simple"),
|
|
|
|
ok = lsm_btree:put(Tree, <<"X">>, <<"Y">>),
|
2012-04-15 10:40:43 +00:00
|
|
|
{ok, Ref} = lsm_btree:sync_range(Tree, #btree_range{from_key= <<"X">>, to_key= <<"X">>}),
|
2012-01-21 20:16:24 +00:00
|
|
|
?assertEqual(ok,
|
|
|
|
receive
|
|
|
|
{fold_done, Ref} -> ok
|
|
|
|
after 1000 -> {error, timeout}
|
|
|
|
end),
|
|
|
|
ok = lsm_btree:close(Tree).
|
2012-01-05 10:48:14 +00:00
|
|
|
|
2012-01-21 20:16:24 +00:00
|
|
|
test_tree_simple_4() ->
|
|
|
|
Key = <<56,11,62,42,35,163,16,100,9,224,8,228,130,94,198,2,126,117,243,
|
|
|
|
1,122,175,79,159,212,177,30,153,71,91,85,233,41,199,190,58,3,
|
|
|
|
173,220,9>>,
|
|
|
|
Value = <<212,167,12,6,105,152,17,80,243>>,
|
|
|
|
{ok, Tree} = lsm_btree:open("simple"),
|
|
|
|
ok = lsm_btree:put(Tree, Key, Value),
|
2012-04-15 14:35:39 +00:00
|
|
|
?assertEqual({ok, Value}, lsm_btree:get(Tree, Key)),
|
2012-01-21 20:16:24 +00:00
|
|
|
ok = lsm_btree:close(Tree).
|
|
|
|
|
|
|
|
test_tree() ->
|
2012-01-19 13:25:47 +00:00
|
|
|
{ok, Tree} = lsm_btree:open("simple2"),
|
2012-01-05 10:48:14 +00:00
|
|
|
lists:foldl(fun(N,_) ->
|
2012-01-07 16:17:48 +00:00
|
|
|
ok = lsm_btree:put(Tree,
|
2012-01-07 11:43:49 +00:00
|
|
|
<<N:128>>, <<"data",N:128>>)
|
2012-01-05 10:48:14 +00:00
|
|
|
end,
|
|
|
|
ok,
|
2012-04-19 17:57:39 +00:00
|
|
|
lists:seq(2,100000,1)),
|
2012-01-15 23:37:52 +00:00
|
|
|
lists:foldl(fun(N,_) ->
|
|
|
|
ok = lsm_btree:put(Tree,
|
|
|
|
<<N:128>>, <<"data",N:128>>)
|
|
|
|
end,
|
|
|
|
ok,
|
|
|
|
lists:seq(4000,6000,1)),
|
|
|
|
|
2012-01-19 13:25:47 +00:00
|
|
|
lsm_btree:delete(Tree, <<1500:128>>),
|
2012-01-15 23:37:52 +00:00
|
|
|
|
2012-01-19 13:25:47 +00:00
|
|
|
{Time,{ok,Count}} = timer:tc(?MODULE, run_fold, [Tree,1000,2000]),
|
2012-01-15 23:37:52 +00:00
|
|
|
|
|
|
|
error_logger:info_msg("time to fold: ~p/sec (time=~p, count=~p)~n", [1000000/(Time/Count), Time/1000000, Count]),
|
|
|
|
|
2012-01-05 10:48:14 +00:00
|
|
|
|
2012-01-07 16:17:48 +00:00
|
|
|
ok = lsm_btree:close(Tree).
|
2012-01-05 10:48:14 +00:00
|
|
|
|
2012-01-15 23:37:52 +00:00
|
|
|
run_fold(Tree,From,To) ->
|
2012-04-15 10:40:43 +00:00
|
|
|
{ok, PID} = lsm_btree:sync_range(Tree, #btree_range{from_key= <<From:128>>, to_key= <<(To+1):128>>}),
|
2012-01-19 13:25:47 +00:00
|
|
|
lists:foreach(fun(1500) -> ok;
|
|
|
|
(N) ->
|
2012-01-15 23:37:52 +00:00
|
|
|
receive
|
2012-01-16 14:13:47 +00:00
|
|
|
{fold_result, PID, <<N:128>>,_} -> ok
|
2012-01-19 13:25:47 +00:00
|
|
|
after 100 ->
|
2012-01-15 23:37:52 +00:00
|
|
|
error_logger:info_msg("timed out on #~p~n", [N])
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
lists:seq(From,To,1)),
|
|
|
|
receive
|
2012-01-16 14:13:47 +00:00
|
|
|
{fold_done, PID} -> ok
|
2012-01-15 23:37:52 +00:00
|
|
|
after 1000 ->
|
2012-01-19 13:25:47 +00:00
|
|
|
error_logger:info_msg("timed out on fold_done! ~n", [])
|
2012-01-15 23:37:52 +00:00
|
|
|
end,
|
2012-01-19 13:25:47 +00:00
|
|
|
|
|
|
|
%% we should now have no spurious messages
|
|
|
|
{messages,[]} = erlang:process_info(self(),messages),
|
|
|
|
|
2012-01-15 23:37:52 +00:00
|
|
|
{ok, To-From}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Command processing
|
|
|
|
%% ----------------------------------------------------------------------
|
|
|
|
cmd_close_args(#state { open = Open }) ->
|
|
|
|
oneof(dict:fetch_keys(Open)).
|
2012-01-04 14:05:31 +00:00
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
cmd_put_args(#state { open = Open }) ->
|
|
|
|
?LET({Name, Key, Value},
|
2012-01-07 21:58:38 +00:00
|
|
|
{oneof(dict:fetch_keys(Open)), g_key(), g_value()},
|
2012-01-07 11:43:49 +00:00
|
|
|
[Name, Key, Value]).
|
|
|
|
|
|
|
|
|
2012-04-15 14:35:39 +00:00
|
|
|
cmd_get_fail_args(#state { open = Open}) ->
|
2012-01-07 22:35:27 +00:00
|
|
|
?LET(Name, g_open_tree(Open),
|
2012-01-07 21:58:38 +00:00
|
|
|
?LET(Key, g_non_existing_key(Name, Open),
|
|
|
|
[Name, Key])).
|
|
|
|
|
2012-04-15 14:35:39 +00:00
|
|
|
cmd_get_args(#state { open = Open}) ->
|
2012-01-07 22:35:27 +00:00
|
|
|
?LET(Name, g_non_empty_btree(Open),
|
2012-01-07 21:58:38 +00:00
|
|
|
?LET(Key, g_existing_key(Name, Open),
|
2012-01-07 11:43:49 +00:00
|
|
|
[Name, Key])).
|
|
|
|
|
|
|
|
cmd_delete_args(#state { open = Open}) ->
|
2012-01-07 22:35:27 +00:00
|
|
|
?LET(Name, g_non_empty_btree(Open),
|
2012-01-07 21:58:38 +00:00
|
|
|
?LET(Key, g_existing_key(Name, Open),
|
2012-01-07 11:43:49 +00:00
|
|
|
[Name, Key])).
|
|
|
|
|
2012-01-21 14:15:19 +00:00
|
|
|
cmd_sync_range_args(#state { open = Open }) ->
|
|
|
|
?LET(Tree, g_non_empty_btree(Open),
|
|
|
|
?LET({K1, K2}, {g_existing_key(Tree, Open),
|
|
|
|
g_existing_key(Tree, Open)},
|
2012-04-15 10:40:43 +00:00
|
|
|
[Tree, #btree_range{from_key=K1, to_key=K2}])).
|
2012-01-07 11:43:49 +00:00
|
|
|
|
2012-01-22 11:34:22 +00:00
|
|
|
cmd_sync_fold_range_args(State) ->
|
2012-04-15 10:40:43 +00:00
|
|
|
?LET([Tree, Range], cmd_sync_range_args(State),
|
2012-01-22 11:34:22 +00:00
|
|
|
?LET({F, Acc0}, g_fold_operation(),
|
2012-04-15 10:40:43 +00:00
|
|
|
[Tree, F, Acc0, Range])).
|
2012-01-22 11:34:22 +00:00
|
|
|
|
2012-01-07 11:43:49 +00:00
|
|
|
%% Context management
|
|
|
|
%% ----------------------------------------------------------------------
|
2012-01-21 20:16:24 +00:00
|
|
|
cleanup_test_trees(#state { open = Open, closed = Closed }) ->
|
|
|
|
[cleanup_tree(N) || N <- dict:fetch_keys(Open)],
|
|
|
|
[cleanup_tree(N) || N <- dict:fetch_keys(Closed)].
|
2012-01-07 11:43:49 +00:00
|
|
|
|
|
|
|
cleanup_tree(Tree) ->
|
2012-01-21 20:16:24 +00:00
|
|
|
case file:list_dir(Tree) of
|
|
|
|
{error, enoent} ->
|
|
|
|
ok;
|
|
|
|
{ok, FileNames} ->
|
|
|
|
[ok = file:delete(filename:join([Tree, Fname]))
|
|
|
|
|| Fname <- FileNames],
|
|
|
|
file:del_dir(Tree)
|
|
|
|
end.
|
2012-01-07 11:43:49 +00:00
|
|
|
|
|
|
|
%% Various Helper routines
|
|
|
|
%% ----------------------------------------------------------------------
|
|
|
|
|
2012-01-07 21:58:38 +00:00
|
|
|
open_dicts_with_keys(#state { open = Open}) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
lists:any(fun({_, #tree { elements = D}}) ->
|
|
|
|
dict:size(D) > 0
|
|
|
|
end,
|
2012-01-07 23:01:18 +00:00
|
|
|
dict:to_list(Open)).
|
2012-01-07 21:58:38 +00:00
|
|
|
|
|
|
|
open_dicts(#state { open = Open}) ->
|
|
|
|
dict:size(Open) > 0.
|
|
|
|
|
|
|
|
closed_dicts(#state { closed = Closed}) ->
|
|
|
|
dict:size(Closed) > 0.
|
|
|
|
|
2012-04-15 10:40:43 +00:00
|
|
|
dict_range_query(Dict, Fun, Acc0, Range) ->
|
|
|
|
KVs = dict_range_query(Dict, Range),
|
2012-01-22 11:34:22 +00:00
|
|
|
lists:foldl(fun({K, V}, Acc) ->
|
|
|
|
Fun(K, V, Acc)
|
|
|
|
end,
|
|
|
|
Acc0,
|
|
|
|
KVs).
|
|
|
|
|
2012-04-15 10:40:43 +00:00
|
|
|
dict_range_query(Dict, Range) ->
|
2012-01-21 14:15:19 +00:00
|
|
|
[{K, V} || {K, V} <- dict:to_list(Dict),
|
2012-04-15 10:40:43 +00:00
|
|
|
?KEY_IN_RANGE(K, Range)].
|
2012-01-21 14:15:19 +00:00
|
|
|
|
2012-01-07 21:58:38 +00:00
|
|
|
|