diff --git a/src/bitcask.erl b/src/bitcask.erl index d82043d0..932c7fdc 100644 --- a/src/bitcask.erl +++ b/src/bitcask.erl @@ -2865,6 +2865,25 @@ corrupt_file(Path, Offset, Data) -> ok = file:write(FH, Data), file:close(FH). +% Verify that if the cached efile port goes away, we can recover +% and not get stuck opening casks +efile_error_test() -> + Dir = "/tmp/bc.efile.error", + B = bitcask:open(Dir, [read_write]), + ok = bitcask:put(B, <<"k">>, <<"v">>), + ok = bitcask:close(B), + Port = get(bitcask_efile_port), + % If this fails, we stopped using the efile port trick to list + % dir contents, so remove this test + ?assert(is_port(Port)), + true = erlang:port_close(Port), + case bitcask:open(Dir) of + {error, _} = Err -> + ?assertEqual(ok, Err); + B2 when is_reference(B2) -> + ok = bitcask:close(B2) + end. + %% About leak_t0(): %% %% If bitcask leaks file descriptors for the 'touch'ed files, output is: diff --git a/src/bitcask_fileops.erl b/src/bitcask_fileops.erl index ff3cf843..b34e2c8a 100644 --- a/src/bitcask_fileops.erl +++ b/src/bitcask_fileops.erl @@ -60,8 +60,8 @@ -include_lib("eqc/include/eqc_fsm.hrl"). -endif. -compile(export_all). --endif. -include_lib("eunit/include/eunit.hrl"). +-endif. %% @doc Open a new file for writing. %% Called on a Dirname, will open a fresh file in that directory. @@ -854,9 +854,20 @@ ensure_dir(F) -> end end. -list_dir(Directory) -> +list_dir(Dir) -> + list_dir(Dir, 1). + +list_dir(_, 0) -> + {error, efile_driver_unavailable}; +list_dir(Directory, Retries) when is_integer(Retries), Retries > 0 -> Port = get_efile_port(), - prim_file:list_dir(Port, Directory). + case prim_file:list_dir(Port, Directory) of + {error, einval} -> + clear_efile_port(), + list_dir(Directory, Retries-1); + Result -> + Result + end. get_efile_port() -> Key = bitcask_efile_port, @@ -875,6 +886,9 @@ get_efile_port() -> Port end. +clear_efile_port() -> + erase(bitcask_efile_port). + prim_file_drv_open(Driver, Portopts) -> try erlang:open_port({spawn, Driver}, Portopts) of Port ->