diff --git a/include/bitcask.hrl b/include/bitcask.hrl index a62efc89..ccca2fba 100644 --- a/include/bitcask.hrl +++ b/include/bitcask.hrl @@ -7,13 +7,13 @@ %% @type filestate(). --record(filestate, {mode :: 'read_only' | 'read_write', % File mode: read_only, read_write +-record(filestate, {mode='read_only' :: 'read_only' | 'read_write', % File mode: read_only, read_write filename :: string(), % Filename - tstamp :: integer(), % Tstamp portion of filename - fd :: port(), % File handle - hintfd :: port(), % File handle for hints + tstamp=0 :: integer(), % Tstamp portion of filename + fd :: port() | undefined, % File handle + hintfd :: port() | undefined, % File handle for hints hintcrc=0 :: integer(), % CRC-32 of current hint - ofs :: non_neg_integer(), % Current offset for writing + ofs=0 :: non_neg_integer(), % Current offset for writing l_ofs=0 :: non_neg_integer(), % Last offset written to data file l_hbytes=0 :: non_neg_integer(),% Last # bytes written to hint file l_hintcrc=0 :: non_neg_integer()}). % CRC-32 of current hint prior to last write diff --git a/rebar.config b/rebar.config index 045c554f..483736e1 100644 --- a/rebar.config +++ b/rebar.config @@ -1,4 +1,5 @@ {erl_opts, [debug_info, warn_untyped_record, + {parse_transform, stacktrace_transform}, {platform_define, "^[0-9]+", namespaced_types}]}. {port_specs, [{"priv/bitcask.so", ["c_src/*.c"]}]}. @@ -20,6 +21,10 @@ {"darwin10.*-32$", "LDFLAGS", "-arch i386"} ]}. +{deps, [ + {stacktrace_compat, "1.0.0"} +]}. + {profiles, [ {prod, diff --git a/rebar.lock b/rebar.lock index 57afcca0..77cd0704 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1 +1,6 @@ -[]. +{"1.1.0", +[{<<"stacktrace_compat">>,{pkg,<<"stacktrace_compat">>,<<"1.0.0">>},0}]}. +[ +{pkg_hash,[ + {<<"stacktrace_compat">>, <<"78E13E0747FABC71DC725A49A4CBDDE54CEC1E9ED13BD80C1D35AC37182E1FA0">>}]} +]. diff --git a/src/bitcask.erl b/src/bitcask.erl index 6e5ebd25..b3a29f74 100644 --- a/src/bitcask.erl +++ b/src/bitcask.erl @@ -75,11 +75,11 @@ %% @type bc_state(). -record(bc_state, {dirname :: string(), write_file :: 'fresh' | 'undefined' | #filestate{}, % File for writing - write_lock :: reference(), % Reference to write lock - read_files :: [#filestate{}], % Files opened for reading + write_lock :: reference() | undefined, % Reference to write lock + read_files=[] :: [#filestate{}], % Files opened for reading max_file_size :: integer(), % Max. size of a written file opts :: list(), % Original options used to open the bitcask - key_transform :: function(), + key_transform=fun kt_id/1 :: fun((binary()) -> binary()), keydir :: reference(), % Key directory read_write_p :: integer(), % integer() avoids atom -> NIF % What tombstone style to write, for testing purposes only. @@ -106,7 +106,7 @@ del_keydir :: reference(), expiry_time :: integer(), expiry_grace_time :: integer(), - key_transform :: function(), + key_transform=fun kt_id/1 :: fun((binary()) -> binary()), read_write_p :: integer(), % integer() avoids atom -> NIF opts :: list(), delete_files :: [#filestate{}]}). @@ -635,9 +635,6 @@ merge1(Dirname, Opts, FilesToMerge0, ExpiredFiles) -> throw({error, not_ready}) end, - LiveRef = make_ref(), - put_state(LiveRef, #bc_state{dirname = Dirname, keydir = LiveKeyDir}), - erlang:erase(LiveRef), {InFiles2,InExpiredFiles} = lists:foldl(fun(F, {InFilesAcc,InExpiredAcc}) -> case lists:member(F#filestate.filename, ExpiredFiles) of @@ -1378,19 +1375,13 @@ merge_files(#mstate { dirname = Dirname, } = State) -> FileId = bitcask_fileops:file_tstamp(File), F = fun(K0, V, Tstamp, Pos, State0) -> - K = try - KT(K0) - catch - Err -> - {key_tx_error, Err} - end, - case K of - {key_tx_error, TxErr} -> + try KT(K0) of + K -> merge_single_entry(K, V, Tstamp, FileId, Pos, State0) + catch + What:Why -> error_logger:error_msg("Invalid key on merge ~p: ~p", - [K0, TxErr]), - State0; - _ -> - merge_single_entry(K, V, Tstamp, FileId, Pos, State0) + [K0, {What, Why}]), + State0 end end, State2 = try bitcask_fileops:fold(File, F, State) of @@ -1969,14 +1960,14 @@ expiry_merge([File | Files], LiveKeyDir, KT, Acc0) -> Fun = fun({tombstone, _}, _, _, Acc) -> Acc; (K0, Tstamp, {Offset, _TotalSz}, Acc) -> - K = try KT(K0) catch TxErr -> {key_tx_error, TxErr} end, - case K of - {key_tx_error, KeyTxErr} -> - error_logger:error_msg("Invalid key on merge ~p: ~p", - [K0, KeyTxErr]); - _ -> + try KT(K0) of + K -> bitcask_nifs:keydir_remove(LiveKeyDir, K, Tstamp, FileId, Offset) + catch + What:Why -> + error_logger:error_msg("Invalid key on merge ~p: ~p", + [K0, {What, Why}]) end, Acc end, @@ -1995,7 +1986,10 @@ expiry_merge([File | Files], LiveKeyDir, KT, Acc0) -> get_key_transform(KT) when is_function(KT) -> - KT; + case erlang:fun_info(KT, arity) of + {arity, 1} -> KT; + _ -> error(invalid_fun) + end; get_key_transform(_State) -> fun kt_id/1. @@ -3074,6 +3068,7 @@ corrupt_file(Path, Offset, Data) -> ok = file:write(FH, Data), file:close(FH). +-ifndef(OTP_RELEASE). %% only applies to OTP20 and earlier % Verify that if the cached efile port goes away, we can recover % and not get stuck opening casks efile_error_test() -> @@ -3092,6 +3087,7 @@ efile_error_test() -> B2 when is_reference(B2) -> ok = bitcask:close(B2) end. +-endif. %% About leak_t0(): %% diff --git a/src/bitcask_file.erl b/src/bitcask_file.erl index 67a668cf..220474bb 100644 --- a/src/bitcask_file.erl +++ b/src/bitcask_file.erl @@ -39,8 +39,8 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --record(state, {fd :: file:fd(), - owner :: pid()}). +-record(state, {fd :: file:fd() | undefined, + owner :: pid() | undefined}). %%%=================================================================== %%% API diff --git a/src/bitcask_fileops.erl b/src/bitcask_fileops.erl index aa48fb3c..a7eeb35d 100644 --- a/src/bitcask_fileops.erl +++ b/src/bitcask_fileops.erl @@ -783,15 +783,25 @@ hintfile_entry(Key, Tstamp, TombInt, Offset, TotalSz) -> TombInt:?TOMBSTONEFIELD_V2, Offset:?OFFSETFIELD_V2>>, Key]. %% =================================================================== -%% file/filelib avoidance code. +%% file/filelib avoidance code. Only needed for pre OTP-21 releases. %% =================================================================== +-ifdef(OTP_RELEASE). +read_file_info(Filename) -> + file:read_file_info(Filename). + +write_file_info(FileName, Info) -> + file:write_file_info(FileName, Info). +-else. read_file_info(FileName) -> prim_file:read_file_info(FileName). write_file_info(FileName, Info) -> prim_file:write_file_info(FileName, Info). +-endif. + + is_file(File) -> case read_file_info(File) of {ok, #file_info{type=regular}} -> @@ -840,6 +850,11 @@ ensure_dir(F) -> list_dir(Dir) -> list_dir(Dir, 1). + +-ifdef(OTP_RELEASE). +list_dir(Directory, Retries) when is_integer(Retries), Retries > 0 -> + file:list_dir(Directory). +-else. list_dir(_, 0) -> {error, efile_driver_unavailable}; list_dir(Directory, Retries) when is_integer(Retries), Retries > 0 -> @@ -880,4 +895,4 @@ prim_file_drv_open(Driver, Portopts) -> error:Reason -> {error, Reason} end. - +-endif.