diff --git a/README.md b/README.md index a72224d..97b7153 100644 --- a/README.md +++ b/README.md @@ -498,19 +498,6 @@ Explain how to build and to run lets with valgrind enabled
-Performance -
--Update driver implementation to use Erlang's asynchronous driver - thread pool for all LevelDB operations. -
-Testing
Update test model to include LevelDB's database, read, and - write options. These options have not undergone any explicit - testing. + write options. These options have not been tested. +
++Update test model to include LevelDB's destroy and repair + operations. These operations have not been tested.
-Performance -
--Update driver implementation to use Erlang's asynchronous driver - thread pool for all LevelDB operations. -
-Testing
Update test model to include LevelDB's database, read, and - write options. These options have not undergone any explicit - testing. + write options. These options have not been tested. +
++Update test model to include LevelDB's destroy and repair + operations. These operations have not been tested.
ets_opt() = set | ordered_set | named_table | {key_pos, pos_integer()} | public | protected | private | compressed+
ets_opt() = set | ordered_set | named_table | {key_pos, pos_integer()} | public | protected | private | compressed | async@@ -138,6 +138,11 @@ is empty, $end_of_table will be returned..
+async only the drv implementation +
+memory only the ets implementation
+async If this option is present, the emulator's async thread +pool will be used when accessing the table data. only the drv +implementation +
+drv If this option is present, the table data will be stored with LevelDB backend via an Erlang Driver. This is the default setting for the table implementation. @@ -482,6 +494,11 @@ __See also:__ [ets:first/1](ets.md#first-1).
+async only the drv implementation +
+memory only the ets implementation
+async If this option is present, the emulator's async thread +pool will be used when accessing the table data. only the drv +implementation +
+drv If this option is present, the table data will be stored with LevelDB backend via an Erlang Driver. This is the default setting for the table implementation. diff --git a/doc/lets_drv.md b/doc/lets_drv.md index 4ccb273..d70a155 100644 --- a/doc/lets_drv.md +++ b/doc/lets_drv.md @@ -26,7 +26,7 @@ -`delete(Tab, Drv) -> any()` +`delete(Tab, Impl) -> any()` @@ -35,7 +35,7 @@ -`delete(Tab, Drv, Key) -> any()` +`delete(Tab, Impl, Key) -> any()` @@ -44,7 +44,7 @@ -`delete_all_objects(Tab, Drv) -> any()` +`delete_all_objects(Tab, Impl) -> any()` @@ -62,7 +62,7 @@ -`first(Tab, Drv) -> any()` +`first(Tab, Impl) -> any()` @@ -71,7 +71,7 @@ -`info_memory(Tab, Drv) -> any()` +`info_memory(Tab, Impl) -> any()` @@ -80,7 +80,7 @@ -`info_size(Tab, Drv) -> any()` +`info_size(Tab, Impl) -> any()` @@ -89,7 +89,7 @@ -`insert(Tab, Drv, Object) -> any()` +`insert(Tab, Impl, Object) -> any()` @@ -98,7 +98,7 @@ -`insert_new(Tab, Drv, Object) -> any()` +`insert_new(Tab, Impl, Object) -> any()` @@ -107,7 +107,7 @@ -`lookup(Tab, Drv, Key) -> any()` +`lookup(Tab, Impl, Key) -> any()` @@ -116,7 +116,7 @@ -`next(Tab, Drv, Key) -> any()` +`next(Tab, Impl, Key) -> any()` @@ -143,5 +143,5 @@ -`tab2list(Tab, Drv) -> any()` +`tab2list(Tab, Impl) -> any()` diff --git a/doc/lets_nif.md b/doc/lets_nif.md index f7b4a42..b9114c8 100644 --- a/doc/lets_nif.md +++ b/doc/lets_nif.md @@ -26,7 +26,7 @@ -`delete(Tab, Nif) -> any()` +`delete(Tab, Impl) -> any()` @@ -35,7 +35,7 @@ -`delete(Tab, Nif, Key) -> any()` +`delete(Tab, Impl, Key) -> any()` @@ -44,7 +44,7 @@ -`delete_all_objects(Tab, Nif) -> any()` +`delete_all_objects(Tab, Impl) -> any()` @@ -62,7 +62,7 @@ -`first(Tab, Nif) -> any()` +`first(Tab, Impl) -> any()` @@ -71,7 +71,7 @@ -`info_memory(Tab, Nif) -> any()` +`info_memory(Tab, Impl) -> any()` @@ -80,7 +80,7 @@ -`info_size(Tab, Nif) -> any()` +`info_size(Tab, Impl) -> any()` @@ -89,7 +89,7 @@ -`insert(Tab, Nif, Object) -> any()` +`insert(Tab, Impl, Object) -> any()` @@ -98,7 +98,7 @@ -`insert_new(Tab, Nif, Object) -> any()` +`insert_new(Tab, Impl, Object) -> any()` @@ -107,7 +107,7 @@ -`lookup(Tab, Nif, Key) -> any()` +`lookup(Tab, Impl, Key) -> any()` @@ -116,7 +116,7 @@ -`next(Tab, Nif, Key) -> any()` +`next(Tab, Impl, Key) -> any()` @@ -143,5 +143,5 @@ -`tab2list(Tab, Nif) -> any()` +`tab2list(Tab, Impl) -> any()` diff --git a/doc/overview.edoc b/doc/overview.edoc index e85f426..6672fd8 100644 --- a/doc/overview.edoc +++ b/doc/overview.edoc @@ -255,15 +255,12 @@ $ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin * Explain how to build and to run lets with valgrind enabled OTP/Erlang virtual machine -- Performance - * Update driver implementation to use Erlang\'s asynchronous driver - thread pool for all LevelDB operations. - - Testing * Functional ** Update test model to include LevelDB\'s database, read, and - write options. These options have not undergone any explicit - testing. + write options. These options have not been tested. + ** Update test model to include LevelDB\'s destroy and repair + operations. These operations have not been tested. * Performance (TBD) * Stability (TBD) diff --git a/src/lets.erl b/src/lets.erl index 63f51f8..b9c8897 100644 --- a/src/lets.erl +++ b/src/lets.erl @@ -50,7 +50,7 @@ -opaque tab() :: #tab{}. -type opts() :: [ets_opt() | impl_opt() | db_opts() | db_read_opts() | db_write_opts()]. --type ets_opt() :: set | ordered_set | named_table | {key_pos,pos_integer()} | public | protected | private | compressed. +-type ets_opt() :: set | ordered_set | named_table | {key_pos,pos_integer()} | public | protected | private | compressed | async. -type impl_opt() :: drv | nif | ets. -type db_opts() :: {db, [{path,file:filename()} | create_if_missing | {create_if_missing,boolean()} | error_if_exists | {error_if_exists,boolean()} | paranoid_checks | {paranoid_checks,boolean()} | {write_buffer_size,pos_integer()} | {max_open_files,pos_integer()} | {block_cache_size,pos_integer()} | {block_size,pos_integer()} | {block_restart_interval,pos_integer()}]}. @@ -98,6 +98,10 @@ %% - +compressed+ If this option is present, the table data will be %% stored in a compressed format. %% +%% - +async+ If this option is present, the emulator\'s async thread +%% pool will be used when accessing the table data. _only the drv +%% implementation_ +%% %% - +drv+ If this option is present, the table data will be stored %% with LevelDB backend via an Erlang Driver. This is the default %% setting for the table implementation. @@ -310,6 +314,7 @@ next(Tab, Key) -> %% - +keypos+ %% - +protection+ %% - +compressed+ +%% - +async+ _only the drv implementation_ %% - +memory+ _only the ets implementation_ %% - +size+ _only the ets implementation_ %% @@ -337,6 +342,8 @@ info(Tab, Item) -> Tab#tab.protection; compressed -> Tab#tab.compressed; + async -> + Tab#tab.async; memory -> Mod:info_memory(Tab, Impl); size -> @@ -414,6 +421,7 @@ create(Op, Name, Opts) -> end end, Compressed = proplists:get_bool(compressed, POpts), + Async = proplists:get_bool(async, POpts), Drv = proplists:get_bool(drv, POpts), Nif = proplists:get_bool(nif, POpts), Ets = proplists:get_bool(ets, POpts), @@ -424,7 +432,8 @@ create(Op, Name, Opts) -> type=Type, keypos=KeyPos, protection=Protection, - compressed=Compressed}, + compressed=Compressed, + async=Async}, DBOptions = fix_db_options(Tab, proplists:get_value(db, POpts, [])), DBReadOptions = proplists:get_value(db_read, POpts, []), @@ -440,8 +449,11 @@ create(Op, Name, Opts) -> lets_drv:Op(Tab, DBOptions, DBReadOptions, DBWriteOptions) end. -fix_db_options(#tab{name=Name, compressed=Compressed}, Options) -> - fix_db_options_compression(Compressed, fix_db_options_path(Name, Options)). +fix_db_options(#tab{name=Name, compressed=Compressed, async=Async}, Options0) -> + Options1 = fix_db_options_path(Name, Options0), + Options2 = fix_db_options_compression(Compressed, Options1), + Options3 = fix_db_options_async(Async, Options2), + Options3. fix_db_options_path(Name, Options) -> case proplists:lookup(path, Options) of @@ -456,6 +468,11 @@ fix_db_options_compression(false, Options) -> fix_db_options_compression(true, Options) -> [{compression, snappy}|Options]. +fix_db_options_async(false, Options) -> + [{async, false}|Options]; +fix_db_options_async(true, Options) -> + [{async, true}|Options]. + binify(X) when is_atom(X) -> list_to_binary(atom_to_list(X)); binify(X) when is_list(X) -> @@ -464,7 +481,7 @@ binify(X) when is_binary(X) -> X. options(Options) -> - Keys = [set, ordered_set, named_table, keypos, public, protected, private, compressed, drv, nif, ets, db, db_read, db_write], + Keys = [set, ordered_set, named_table, keypos, public, protected, private, compressed, async, drv, nif, ets, db, db_read, db_write], options(Options, Keys). options(Options, Keys) when is_list(Options) -> diff --git a/src/lets.hrl b/src/lets.hrl index 00b1479..2dd4510 100644 --- a/src/lets.hrl +++ b/src/lets.hrl @@ -32,6 +32,7 @@ keypos=1 :: pos_integer(), protection=protected :: public|protected|private, compressed=false :: boolean(), + async=false :: boolean(), drv :: port() | undefined, nif :: nif() | undefined, ets :: ets:tab() | undefined diff --git a/src/lets_drv.erl b/src/lets_drv.erl index f4bfdd7..7c4f706 100644 --- a/src/lets_drv.erl +++ b/src/lets_drv.erl @@ -49,6 +49,7 @@ -define(LETS_BADARG, 16#00). -define(LETS_TRUE, 16#01). -define(LETS_END_OF_TABLE, 16#02). +-define(LETS_BINARY, 16#03). -define(LETS_OPEN6, 16#00). -define(LETS_DESTROY6, 16#01). @@ -96,9 +97,9 @@ init() -> open(#tab{name=_Name, named_table=_Named, type=Type, protection=Protection}=Tab, Options, ReadOptions, WriteOptions) -> {value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options), - Drv = impl_open(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions), - %% @TODO implement named Drv (of sorts) - Tab#tab{drv=Drv}. + Impl = impl_open(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions), + %% @TODO implement named Impl (of sorts) + Tab#tab{drv=Impl}. destroy(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOptions) -> {value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options), @@ -108,81 +109,81 @@ repair(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOption {value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options), impl_repair(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions). -insert(#tab{keypos=KeyPos, type=Type}, Drv, Object) when is_tuple(Object) -> +insert(#tab{keypos=KeyPos, type=Type}, Impl, Object) when is_tuple(Object) -> Key = element(KeyPos,Object), Val = Object, - impl_insert(Drv, encode(Type, Key), encode(Type, Val)); -insert(#tab{keypos=KeyPos, type=Type}, Drv, Objects) when is_list(Objects) -> + impl_insert(Impl, encode(Type, Key), encode(Type, Val)); +insert(#tab{keypos=KeyPos, type=Type}, Impl, Objects) when is_list(Objects) -> List = [{encode(Type, element(KeyPos,Object)), encode(Type, Object)} || Object <- Objects ], - impl_insert(Drv, List). + impl_insert(Impl, List). -insert_new(#tab{keypos=KeyPos, type=Type}, Drv, Object) when is_tuple(Object) -> +insert_new(#tab{keypos=KeyPos, type=Type}, Impl, Object) when is_tuple(Object) -> Key = element(KeyPos,Object), Val = Object, - impl_insert_new(Drv, encode(Type, Key), encode(Type, Val)); -insert_new(#tab{keypos=KeyPos, type=Type}, Drv, Objects) when is_list(Objects) -> + impl_insert_new(Impl, encode(Type, Key), encode(Type, Val)); +insert_new(#tab{keypos=KeyPos, type=Type}, Impl, Objects) when is_list(Objects) -> List = [{encode(Type, element(KeyPos,Object)), encode(Type, Object)} || Object <- Objects ], - impl_insert_new(Drv, List). + impl_insert_new(Impl, List). -delete(_Tab, Drv) -> - impl_delete(Drv). +delete(_Tab, Impl) -> + impl_delete(Impl). -delete(#tab{type=Type}, Drv, Key) -> - impl_delete(Drv, encode(Type, Key)). +delete(#tab{type=Type}, Impl, Key) -> + impl_delete(Impl, encode(Type, Key)). -delete_all_objects(_Tab, Drv) -> - impl_delete_all_objects(Drv). +delete_all_objects(_Tab, Impl) -> + impl_delete_all_objects(Impl). -lookup(#tab{type=Type}, Drv, Key) -> - case impl_lookup(Drv, encode(Type, Key)) of - true -> +lookup(#tab{type=Type}, Impl, Key) -> + case impl_lookup(Impl, encode(Type, Key)) of + '$end_of_table' -> []; Object when is_binary(Object) -> [decode(Type, Object)] end. -first(#tab{type=Type}, Drv) -> - case impl_first(Drv) of +first(#tab{type=Type}, Impl) -> + case impl_first(Impl) of '$end_of_table' -> '$end_of_table'; Key -> decode(Type, Key) end. -next(#tab{type=Type}, Drv, Key) -> - case impl_next(Drv, encode(Type, Key)) of +next(#tab{type=Type}, Impl, Key) -> + case impl_next(Impl, encode(Type, Key)) of '$end_of_table' -> '$end_of_table'; Next -> decode(Type, Next) end. -info_memory(_Tab, Drv) -> - case impl_info_memory(Drv) of +info_memory(_Tab, Impl) -> + case impl_info_memory(Impl) of Memory when is_integer(Memory) -> erlang:round(Memory / erlang:system_info(wordsize)); Else -> Else end. -info_size(_Tab, Drv) -> - impl_info_size(Drv). +info_size(_Tab, Impl) -> + impl_info_size(Impl). -tab2list(Tab, Drv) -> - tab2list(Tab, Drv, impl_first(Drv), []). +tab2list(Tab, Impl) -> + tab2list(Tab, Impl, impl_first(Impl), []). -tab2list(_Tab, _Drv, '$end_of_table', Acc) -> +tab2list(_Tab, _Impl, '$end_of_table', Acc) -> lists:reverse(Acc); -tab2list(#tab{type=Type}=Tab, Drv, Key, Acc) -> +tab2list(#tab{type=Type}=Tab, Impl, Key, Acc) -> NewAcc = - case impl_lookup(Drv, Key) of - true -> + case impl_lookup(Impl, Key) of + '$end_of_table' -> %% @NOTE This is not an atomic operation Acc; Object when is_binary(Object) -> [decode(Type, Object)|Acc] end, - tab2list(Tab, Drv, impl_next(Drv, Key), NewAcc). + tab2list(Tab, Impl, impl_next(Impl, Key), NewAcc). %%%---------------------------------------------------------------------- @@ -200,79 +201,73 @@ decode(ordered_set, Term) -> sext:decode(Term). impl_open(Type, Protection, Path, Options, ReadOptions, WriteOptions) -> - Drv = init(), - true = call(Drv, {?LETS_OPEN6, Type, Protection, Path, Options, ReadOptions, WriteOptions}), - Drv. + Impl = init(), + true = call(Impl, {?LETS_OPEN6, Type, Protection, Path, Options, ReadOptions, WriteOptions}), + Impl. impl_destroy(Type, Protection, Path, Options, ReadOptions, WriteOptions) -> - Drv = init(), - true = call(Drv, {?LETS_OPEN6, Type, Protection, Path, Options, ReadOptions, WriteOptions}), - _ = port_close(Drv), + Impl = init(), + true = call(Impl, {?LETS_OPEN6, Type, Protection, Path, Options, ReadOptions, WriteOptions}), + _ = port_close(Impl), _ = erl_ddll:unload(lets_drv), true. impl_repair(Type, Protection, Path, Options, ReadOptions, WriteOptions) -> - Drv = init(), - true = call(Drv, {?LETS_REPAIR6, Type, Protection, Path, Options, ReadOptions, WriteOptions}), - _ = port_close(Drv), + Impl = init(), + true = call(Impl, {?LETS_REPAIR6, Type, Protection, Path, Options, ReadOptions, WriteOptions}), + _ = port_close(Impl), _ = erl_ddll:unload(lets_drv), true. -impl_insert(Drv, Key, Object) -> - call(Drv, {?LETS_INSERT3, Key, Object}). +impl_insert(Impl, Key, Object) -> + call(Impl, {?LETS_INSERT3, Key, Object}). -impl_insert(Drv, List) -> - call(Drv, {?LETS_INSERT2, List}). +impl_insert(Impl, List) -> + call(Impl, {?LETS_INSERT2, List}). -impl_insert_new(Drv, Key, Object) -> - call(Drv, {?LETS_INSERT_NEW3, Key, Object}). +impl_insert_new(Impl, Key, Object) -> + call(Impl, {?LETS_INSERT_NEW3, Key, Object}). -impl_insert_new(Drv, List) -> - call(Drv, {?LETS_INSERT_NEW2, List}). +impl_insert_new(Impl, List) -> + call(Impl, {?LETS_INSERT_NEW2, List}). -impl_delete(Drv) -> - Res = call(Drv, {?LETS_DELETE1}), - _ = port_close(Drv), +impl_delete(Impl) -> + Res = call(Impl, {?LETS_DELETE1}), + _ = port_close(Impl), _ = erl_ddll:unload(lets_drv), Res. -impl_delete(Drv, Key) -> - call(Drv, {?LETS_DELETE2, Key}). +impl_delete(Impl, Key) -> + call(Impl, {?LETS_DELETE2, Key}). -impl_delete_all_objects(Drv) -> - call(Drv, {?LETS_DELETE_ALL_OBJECTS1}). +impl_delete_all_objects(Impl) -> + call(Impl, {?LETS_DELETE_ALL_OBJECTS1}). -impl_lookup(Drv, Key) -> - call(Drv, {?LETS_LOOKUP2, Key}). +impl_lookup(Impl, Key) -> + call(Impl, {?LETS_LOOKUP2, Key}). -impl_first(Drv) -> - call(Drv, {?LETS_FIRST1}). +impl_first(Impl) -> + call(Impl, {?LETS_FIRST1}). -impl_next(Drv, Key) -> - call(Drv, {?LETS_NEXT2, Key}). +impl_next(Impl, Key) -> + call(Impl, {?LETS_NEXT2, Key}). -impl_info_memory(Drv) -> - call(Drv, {?LETS_INFO_MEMORY1}). +impl_info_memory(Impl) -> + call(Impl, {?LETS_INFO_MEMORY1}). -impl_info_size(Drv) -> - call(Drv, {?LETS_INFO_SIZE1}). +impl_info_size(Impl) -> + call(Impl, {?LETS_INFO_SIZE1}). -call(Drv, Tuple) -> +call(Impl, Tuple) -> Data = term_to_binary(Tuple), - port_command(Drv, Data), + port_command(Impl, Data), receive - {Drv, ?LETS_TRUE, Reply} -> + {Impl, ?LETS_BINARY, Reply} -> Reply; - {Drv, ?LETS_TRUE} -> + {Impl, ?LETS_TRUE} -> true; - {Drv, ?LETS_END_OF_TABLE} -> + {Impl, ?LETS_END_OF_TABLE} -> '$end_of_table'; - {Drv, ?LETS_BADARG} -> - erlang:error(badarg, [Drv]) - %% after 1000 -> - %% receive X -> - %% erlang:error(timeout, [Drv, X]) - %% after 0 -> - %% erlang:error(timeout, [Drv]) - %% end + {Impl, ?LETS_BADARG} -> + erlang:error(badarg, [Impl]) end. diff --git a/src/lets_nif.erl b/src/lets_nif.erl index 2fd7ddd..f8e316f 100644 --- a/src/lets_nif.erl +++ b/src/lets_nif.erl @@ -67,9 +67,9 @@ init() -> open(#tab{name=_Name, named_table=_Named, type=Type, protection=Protection}=Tab, Options, ReadOptions, WriteOptions) -> {value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options), - Nif = impl_open(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions), - %% @TODO implement named Nif (of sorts) - Tab#tab{nif=Nif}. + Impl = impl_open(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions), + %% @TODO implement named Impl (of sorts) + Tab#tab{nif=Impl}. destroy(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOptions) -> {value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options), @@ -79,81 +79,81 @@ repair(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOption {value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options), impl_repair(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions). -insert(#tab{keypos=KeyPos, type=Type}, Nif, Object) when is_tuple(Object) -> +insert(#tab{keypos=KeyPos, type=Type}, Impl, Object) when is_tuple(Object) -> Key = element(KeyPos,Object), Val = Object, - impl_insert(Nif, encode(Type, Key), encode(Type, Val)); -insert(#tab{keypos=KeyPos, type=Type}, Nif, Objects) when is_list(Objects) -> + impl_insert(Impl, encode(Type, Key), encode(Type, Val)); +insert(#tab{keypos=KeyPos, type=Type}, Impl, Objects) when is_list(Objects) -> List = [{encode(Type, element(KeyPos,Object)), encode(Type, Object)} || Object <- Objects ], - impl_insert(Nif, List). + impl_insert(Impl, List). -insert_new(#tab{keypos=KeyPos, type=Type}, Nif, Object) when is_tuple(Object) -> +insert_new(#tab{keypos=KeyPos, type=Type}, Impl, Object) when is_tuple(Object) -> Key = element(KeyPos,Object), Val = Object, - impl_insert_new(Nif, encode(Type, Key), encode(Type, Val)); -insert_new(#tab{keypos=KeyPos, type=Type}, Nif, Objects) when is_list(Objects) -> + impl_insert_new(Impl, encode(Type, Key), encode(Type, Val)); +insert_new(#tab{keypos=KeyPos, type=Type}, Impl, Objects) when is_list(Objects) -> List = [{encode(Type, element(KeyPos,Object)), encode(Type, Object)} || Object <- Objects ], - impl_insert_new(Nif, List). + impl_insert_new(Impl, List). -delete(_Tab, Nif) -> - impl_delete(Nif). +delete(_Tab, Impl) -> + impl_delete(Impl). -delete(#tab{type=Type}, Nif, Key) -> - impl_delete(Nif, encode(Type, Key)). +delete(#tab{type=Type}, Impl, Key) -> + impl_delete(Impl, encode(Type, Key)). -delete_all_objects(_Tab, Nif) -> - impl_delete_all_objects(Nif). +delete_all_objects(_Tab, Impl) -> + impl_delete_all_objects(Impl). -lookup(#tab{type=Type}, Nif, Key) -> - case impl_lookup(Nif, encode(Type, Key)) of - true -> +lookup(#tab{type=Type}, Impl, Key) -> + case impl_lookup(Impl, encode(Type, Key)) of + '$end_of_table' -> []; Object when is_binary(Object) -> [decode(Type, Object)] end. -first(#tab{type=Type}, Nif) -> - case impl_first(Nif) of +first(#tab{type=Type}, Impl) -> + case impl_first(Impl) of '$end_of_table' -> '$end_of_table'; Key -> decode(Type, Key) end. -next(#tab{type=Type}, Nif, Key) -> - case impl_next(Nif, encode(Type, Key)) of +next(#tab{type=Type}, Impl, Key) -> + case impl_next(Impl, encode(Type, Key)) of '$end_of_table' -> '$end_of_table'; Next -> decode(Type, Next) end. -info_memory(_Tab, Nif) -> - case impl_info_memory(Nif) of +info_memory(_Tab, Impl) -> + case impl_info_memory(Impl) of Memory when is_integer(Memory) -> erlang:round(Memory / erlang:system_info(wordsize)); Else -> Else end. -info_size(_Tab, Nif) -> - impl_info_size(Nif). +info_size(_Tab, Impl) -> + impl_info_size(Impl). -tab2list(Tab, Nif) -> - tab2list(Tab, Nif, impl_first(Nif), []). +tab2list(Tab, Impl) -> + tab2list(Tab, Impl, impl_first(Impl), []). -tab2list(_Tab, _Nif, '$end_of_table', Acc) -> +tab2list(_Tab, _Impl, '$end_of_table', Acc) -> lists:reverse(Acc); -tab2list(#tab{type=Type}=Tab, Nif, Key, Acc) -> +tab2list(#tab{type=Type}=Tab, Impl, Key, Acc) -> NewAcc = - case impl_lookup(Nif, Key) of - true -> + case impl_lookup(Impl, Key) of + '$end_of_table' -> %% @NOTE This is not an atomic operation Acc; Object when is_binary(Object) -> [decode(Type, Object)|Acc] end, - tab2list(Tab, Nif, impl_next(Nif, Key), NewAcc). + tab2list(Tab, Impl, impl_next(Impl, Key), NewAcc). %%%---------------------------------------------------------------------- @@ -182,38 +182,38 @@ impl_destroy(_Type, _Protection, _Path, _Options, _ReadOptions, _WriteOptions) - impl_repair(_Type, _Protection, _Path, _Options, _ReadOptions, _WriteOptions) -> ?NIF_STUB. -impl_insert(_Nif, _Key, _Object) -> +impl_insert(_Impl, _Key, _Object) -> ?NIF_STUB. -impl_insert(_Nif, _List) -> +impl_insert(_Impl, _List) -> ?NIF_STUB. -impl_insert_new(_Nif, _Key, _Object) -> +impl_insert_new(_Impl, _Key, _Object) -> ?NIF_STUB. -impl_insert_new(_Nif, _List) -> +impl_insert_new(_Impl, _List) -> ?NIF_STUB. -impl_delete(_Nif) -> +impl_delete(_Impl) -> ?NIF_STUB. -impl_delete(_Nif, _Key) -> +impl_delete(_Impl, _Key) -> ?NIF_STUB. -impl_delete_all_objects(_Nif) -> +impl_delete_all_objects(_Impl) -> ?NIF_STUB. -impl_lookup(_Nif, _Key) -> +impl_lookup(_Impl, _Key) -> ?NIF_STUB. -impl_first(_Nif) -> +impl_first(_Impl) -> ?NIF_STUB. -impl_next(_Nif, _Key) -> +impl_next(_Impl, _Key) -> ?NIF_STUB. -impl_info_memory(_Nif) -> +impl_info_memory(_Impl) -> ?NIF_STUB. -impl_info_size(_Nif) -> +impl_info_size(_Impl) -> ?NIF_STUB. diff --git a/test/qc/qc_leveldb.erl b/test/qc/qc_leveldb.erl index 3ae4475..dad2a90 100644 --- a/test/qc/qc_leveldb.erl +++ b/test/qc/qc_leveldb.erl @@ -30,6 +30,8 @@ %% lets , open/0, open/1 , reopen/0, reopen/1 + , destroy/0, destroy/1 + , repair/0, repair/1 , close/1 , put/2, put/3 , delete/2, delete/3 @@ -106,6 +108,40 @@ reopen(Options) -> free_ptr(ErrPtr) end. +destroy() -> + Options = leveldb:leveldb_options_create(), + try + destroy(Options) + after + leveldb:leveldb_options_destroy(Options) + end. + +destroy(Options) -> + ErrPtr = errptr(), + try + leveldb:leveldb_destroy(Options, ?MODULE_STRING, ErrPtr), + read_errptr(ErrPtr) + after + free_ptr(ErrPtr) + end. + +repair() -> + Options = leveldb:leveldb_options_create(), + try + repair(Options) + after + leveldb:leveldb_options_repair(Options) + end. + +repair(Options) -> + ErrPtr = errptr(), + try + leveldb:leveldb_repair(Options, ?MODULE_STRING, ErrPtr), + read_errptr(ErrPtr) + after + free_ptr(ErrPtr) + end. + close(Db) -> ok == leveldb:leveldb_close(Db). diff --git a/test/qc/qc_statem_lets.erl b/test/qc/qc_statem_lets.erl index 76c4424..58a7f9e 100644 --- a/test/qc/qc_statem_lets.erl +++ b/test/qc/qc_statem_lets.erl @@ -30,6 +30,9 @@ -export([initial_state/0, state_is_sane/1, next_state/3, precondition/2, postcondition/3]). -export([commands_setup/1, commands_teardown/1, commands_teardown/2]). +%% @TODO remove at time of db, db_read, db_write options testing +-compile(export_all). + %% @NOTE For boilerplate exports, see "qc_statem.hrl" -include_lib("qc/include/qc_statem.hrl"). @@ -75,16 +78,18 @@ command_gen(Mod,#state{parallel=true}=S) -> serial_command_gen(_Mod,#state{tab=undefined, type=undefined, impl=undefined}=S) -> {call,?IMPL,new,[?TAB,gen_options(new,S)]}; serial_command_gen(_Mod,#state{tab=undefined}=S) -> - {call,?IMPL,new,[undefined,?TAB,gen_options(new,S)]}; + oneof([{call,?IMPL,new,[undefined,?TAB,gen_options(new,S)]}] + %% @TODO ++ [{call,?IMPL,destroy,[undefined,?TAB,gen_options(destroy,S)]}] + %% @TODO ++ [{call,?IMPL,repair,[undefined,?TAB,gen_options(repair,S)]}] + ); serial_command_gen(_Mod,#state{tab=Tab, type=Type}=S) -> %% @TODO insert/3, insert_new/3, delete/3, delete_all_objs/2 write_gen_options %% @TODO lookup/3 read_gen_options oneof([{call,?IMPL,insert,[Tab,oneof([gen_obj(S),gen_objs(S)])]}] - ++ [{call,?IMPL,insert_new,[Tab,oneof([gen_obj(S),gen_objs(S)])]} || Type == ets] - %% @TODO ++ [{call,?IMPL,delete,[Tab]}] + ++ [{call,?IMPL,insert_new,[Tab,oneof([gen_obj(S),gen_objs(S)])]} || Type =:= ets] ++ [{call,?IMPL,delete,[Tab]}] ++ [{call,?IMPL,delete,[Tab,gen_key(S)]}] - ++ [{call,?IMPL,delete_all_objs,[Tab]} || Type == ets] + ++ [{call,?IMPL,delete_all_objs,[Tab]} || Type =:= ets] ++ [{call,?IMPL,lookup,[Tab,gen_key(S)]}] ++ [{call,?IMPL,first,[Tab]}] ++ [{call,?IMPL,next,[Tab,gen_key(S)]}] @@ -98,9 +103,9 @@ parallel_command_gen(_Mod,#state{tab=Tab, type=Type}=S) -> %% @TODO insert/3, insert_new/3, delete_all_objs/2 write_gen_options %% @TODO lookup/3 read_gen_options oneof([{call,?IMPL,insert,[Tab,oneof([gen_obj(S),gen_objs(S)])]}] - ++ [{call,?IMPL,insert_new,[Tab,oneof([gen_obj(S),gen_objs(S)])]} || Type == ets] + ++ [{call,?IMPL,insert_new,[Tab,oneof([gen_obj(S),gen_objs(S)])]} || Type =:= ets] ++ [{call,?IMPL,delete,[Tab,gen_key(S)]}] - ++ [{call,?IMPL,delete_all_objs,[Tab]} || Type == ets] + ++ [{call,?IMPL,delete_all_objs,[Tab]} || Type =:= ets] ++ [{call,?IMPL,lookup,[Tab,gen_key(S)]}] ++ [{call,?IMPL,first,[Tab]}] ++ [{call,?IMPL,next,[Tab,gen_key(S)]}] @@ -152,7 +157,7 @@ next_state(#state{tab=undefined}=S, V, {call,_,new,[_Tab,?TAB,Options]}) -> end, S#state{type=Type, impl=Impl, exists=true, tab=V}; next_state(#state{impl=Impl}=S, _V, {call,_,destroy,[_Tab,?TAB,_Options]}) - when Impl /= ets -> + when Impl =/= ets -> S#state{tab=undefined, exists=false, objs=[]}; next_state(S, _V, {call,_,insert,[_Tab,Objs]}) when is_list(Objs) -> insert_objs(S, Objs); @@ -181,12 +186,18 @@ next_state(S, _V, {call,_,_,_}) -> precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,new,[?TAB,Options]}) -> L = proplists:get_value(db, Options, []), proplists:get_bool(create_if_missing, L) andalso proplists:get_bool(error_if_exists, L); +precondition(#state{tab=Tab}, {call,_,new,[?TAB,_Options]}) -> + Tab =:= undefined; precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,new,[_Tab,?TAB,Options]}) -> L = proplists:get_value(db, Options, []), proplists:get_bool(create_if_missing, L) andalso proplists:get_bool(error_if_exists, L); -precondition(#state{tab=Tab}, {call,_,new,[?TAB,_Options]}) when Tab /= undefined -> +precondition(#state{tab=Tab}, {call,_,new,[_Tab,?TAB,_Options]}) -> + Tab =:= undefined; +precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,new,[_Tab,?TAB,_Options]}) -> false; -precondition(#state{tab=Tab}, {call,_,new,[_Tab,?TAB,_Options]}) when Tab /= undefined -> +precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,destroy,[_Tab,?TAB,_Options]}) -> + false; +precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,repair,[_Tab,?TAB,_Options]}) -> false; precondition(_S, {call,_,_,_}) -> true. @@ -252,7 +263,7 @@ postcondition(#state{type=ordered_set}=S, {call,_,next,[_Tab, Key]}, Res) -> Res =:= K end; postcondition(#state{type=set}=S, {call,_,tab2list,[_Tab]}, Res) -> - [] == (S#state.objs -- Res); + [] =:= (S#state.objs -- Res); postcondition(#state{type=ordered_set}=S, {call,_,tab2list,[_Tab]}, Res) -> sort(S) =:= Res; postcondition(_S, {call,_,_,_}, _Res) -> @@ -281,28 +292,35 @@ gen_options(Op,#state{tab=undefined, type=undefined, impl=undefined}=S) -> ?LET({Type,Impl}, {gen_ets_type(), gen_ets_impl()}, gen_options(Op,S#state{type=Type, impl=Impl})); gen_options(Op,#state{type=Type, impl=drv=Impl}=S) -> - [Type, public, named_table, {keypos,#obj.key}, Impl] + [Type, public, named_table, {keypos,#obj.key}, + {compressed, gen_boolean()}, {async, gen_boolean()}, Impl] ++ gen_leveldb_options(Op,S); gen_options(Op,#state{type=Type, impl=nif=Impl}=S) -> - [Type, public, named_table, {keypos,#obj.key}, Impl] + [Type, public, named_table, {keypos,#obj.key}, + {compressed, gen_boolean()}, {async, gen_boolean()}, Impl] ++ gen_leveldb_options(Op,S); gen_options(_Op,#state{type=Type, impl=ets=Impl}) -> - [Type, public, named_table, {keypos,#obj.key}, Impl]. + [Type, public, named_table, {keypos,#obj.key}, + {compressed, gen_boolean()}, Impl]. gen_leveldb_options(Op,S) -> [gen_db_options(Op,S), gen_db_read_options(Op,S), gen_db_write_options(Op,S)]. gen_db_options(new,#state{exists=Exists}) -> ExistsOptions = if Exists -> []; true -> [create_if_missing, error_if_exists] end, - ?LET(Options, ulist(gen_db_options()), {db, Options ++ ExistsOptions}); + %% @TODO ?LET(Options, ulist(gen_db_options()), {db, Options ++ ExistsOptions}); + {db, ExistsOptions}; gen_db_options(_Op,_S) -> - ?LET(Options, ulist(gen_db_options()), {db, Options}). + %% @TODO ?LET(Options, ulist(gen_db_options()), {db, Options}). + {db, []} . gen_db_read_options(_Op,_S) -> - ?LET(Options, ulist(gen_db_read_options()), {db_read, Options}). + %% @TODO ?LET(Options, ulist(gen_db_read_options()), {db_read, Options}). + {db_read, []}. gen_db_write_options(_Op,_S) -> - ?LET(Options, ulist(gen_db_write_options()), {db_write, Options}). + %% @TODO ?LET(Options, ulist(gen_db_write_options()), {db_write, Options}). + {db_write, []}. gen_db_options() -> oneof([paranoid_checks, {paranoid_checks,gen_boolean()}, {write_buffer_size,gen_pos_integer()}, {max_open_files,gen_pos_integer()}, {block_cache_size,gen_pos_integer()}, {block_size,gen_pos_integer()}, {block_restart_interval,gen_pos_integer()}]). @@ -412,7 +430,7 @@ keyfind(X, #state{objs=L}=S) -> lists:filter(fun(#obj{key=K}) -> eq(X, K, S) end, L). keymember(X, S) -> - [] /= keyfind(X, S). + [] =/= keyfind(X, S). eq(X, Y, #state{type=set, impl=ets}) -> X =:= Y; diff --git a/test/qc/qc_statemc_lets.erl b/test/qc/qc_statemc_lets.erl index f2b6397..a883830 100644 --- a/test/qc/qc_statemc_lets.erl +++ b/test/qc/qc_statemc_lets.erl @@ -66,7 +66,10 @@ command_gen(Mod,#state{parallel=true}=S) -> serial_command_gen(_Mod,#state{db=undefined, exists=false}) -> {call,?IMPL,open,[]}; serial_command_gen(_Mod,#state{db=undefined, exists=true}) -> - {call,?IMPL,reopen,[]}; + oneof([{call,?IMPL,reopen,[]} + %% @TODO {call,?IMPL,destroy,[]}, + %% @TODO {call,?IMPL,repair,[]} + ]); serial_command_gen(_Mod,#state{db=Db}=S) -> oneof([{call,?IMPL,close,[Db]}, {call,?IMPL,put,[Db,gen_obj(S)]}, @@ -100,6 +103,8 @@ next_state(#state{db=undefined, exists=false}=S, V, {call,_,open,[]}) -> S#state{db=V, exists=true}; next_state(#state{db=undefined, exists=true}=S, V, {call,_,reopen,[]}) -> S#state{db=V, exists=true}; +next_state(#state{db=undefined, exists=true}=S, V, {call,_,destroy,[]}) -> + S#state{db=V, exists=false, objs=[]}; next_state(#state{db=Db}=S, _V, {call,_,close,[Db]}) when Db /= undefined -> S#state{db=undefined}; next_state(S, _V, {call,_,put,[_Db,Obj]}) -> @@ -114,10 +119,18 @@ precondition(#state{exists=true}, {call,_,open,[]}) -> false; precondition(#state{exists=false}, {call,_,reopen,[]}) -> false; +precondition(#state{exists=false}, {call,_,destroy,[]}) -> + false; +precondition(#state{exists=false}, {call,_,repair,[]}) -> + false; precondition(#state{db=Db}, {call,_,open,[]}) when Db /= undefined-> false; precondition(#state{db=Db}, {call,_,reopen,[]}) when Db /= undefined-> false; +precondition(#state{db=Db}, {call,_,destroy,[]}) when Db /= undefined-> + false; +precondition(#state{db=Db}, {call,_,repair,[]}) when Db /= undefined-> + false; precondition(_S, {call,_,_,_}) -> true. @@ -126,6 +139,10 @@ postcondition(#state{exists=false}, {call,_,open,[]}, Res) -> ?IMPL:is_db(Res); postcondition(#state{exists=true}, {call,_,reopen,[]}, Res) -> ?IMPL:is_db(Res); +postcondition(#state{exists=true}, {call,_,destroy,[]}, Res) -> + Res; +postcondition(#state{exists=true}, {call,_,repair,[]}, Res) -> + Res; postcondition(#state{db=Db}, {call,_,close,[_Db]}, Res) -> Res andalso Db /= undefined; postcondition(_S, {call,_,put,[_Db,_]}, Res) ->