Skip to content

Commit 2be3c41

Browse files
committed
Merge pull request #198 from basho/bugfix/list-files-race
Fix race listing readable files Reviewed-by: slfritchie
2 parents 4334c1c + 694480f commit 2be3c41

File tree

1 file changed

+50
-13
lines changed

1 file changed

+50
-13
lines changed

src/bitcask.erl

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,19 +1335,9 @@ get_filestate(FileId, Dirname, ReadFiles, Mode) ->
13351335

13361336

13371337
list_data_files(Dirname, WritingFile, MergingFile) ->
1338-
%% Get list of {tstamp, filename} for all files in the directory then
1339-
%% reverse sort that list and extract the fully-qualified filename.
13401338
Files1 = bitcask_fileops:data_file_tstamps(Dirname),
1341-
Files2 = bitcask_fileops:data_file_tstamps(Dirname),
1342-
% TODO: Remove crazy
1343-
if Files1 == Files2 ->
1344-
%% No race, Files1 is a stable list.
1345-
[F || {_Tstamp, F} <- lists:sort(Files1),
1346-
F /= WritingFile,
1347-
F /= MergingFile];
1348-
true ->
1349-
list_data_files(Dirname, WritingFile, MergingFile)
1350-
end.
1339+
[F || {_Tstamp, F} <- lists:sort(Files1),
1340+
F /= WritingFile, F /= MergingFile].
13511341

13521342
merge_files(#mstate { input_files = [] } = State) ->
13531343
State;
@@ -1668,7 +1658,16 @@ readable_and_setuid_files(Dirname) ->
16681658
%% Filter out files with setuid bit set: they've been marked for
16691659
%% deletion by an earlier *successful* merge.
16701660
Fs = [F || F <- list_data_files(Dirname, WritingFile, MergingFile)],
1671-
lists:partition(fun(F) -> not has_pending_delete_bit(F) end, Fs).
1661+
1662+
WritingFile2 = bitcask_lockops:read_activefile(write, Dirname),
1663+
MergingFile2 = bitcask_lockops:read_activefile(merge, Dirname),
1664+
case {WritingFile2, MergingFile2} of
1665+
{WritingFile, MergingFile} ->
1666+
lists:partition(fun(F) -> not has_pending_delete_bit(F) end, Fs);
1667+
_ ->
1668+
% Changed while fetching file list, retry
1669+
readable_and_setuid_files(Dirname)
1670+
end.
16721671

16731672
%% Internal put - have validated that the file is opened for write
16741673
%% and looked up the state at this point
@@ -2049,6 +2048,44 @@ list_data_files_test2() ->
20492048
%% Now use the list_data_files to scan the dir
20502049
ExpFiles = list_data_files("/tmp/bc.test.list", undefined, undefined).
20512050

2051+
% Test that readable_files will not return the currently active
2052+
% write or merge file by mistake if they change in between fetching them
2053+
% and listing the files in the directory.
2054+
list_data_files_race_test() ->
2055+
Dir = "/tmp/bc.test.list.race",
2056+
Fname = fun(N) ->
2057+
filename:join(Dir, integer_to_list(N) ++ ".bitcask.data")
2058+
end,
2059+
WriteFile = fun(N) ->
2060+
ok = file:write_file(Fname(N), <<>>)
2061+
end,
2062+
WriteFiles = fun(S,E) ->
2063+
[WriteFile(N) || N <- lists:seq(S, E)]
2064+
end,
2065+
os:cmd("rm -rf " ++ Dir ++ "; mkdir -p " ++ Dir),
2066+
WriteFiles(1,5),
2067+
% Faking 4 as merge file, 5 as write file,
2068+
% then switching to 6 as merge, 7 as write
2069+
KindN = fun(merge) -> 4; (write) -> 5 end,
2070+
meck:new(bitcask_lockops, [passthrough]),
2071+
meck:expect(bitcask_lockops, read_activefile,
2072+
fun(Kind, _) ->
2073+
case get({fake_activefile, Kind}) of
2074+
undefined ->
2075+
N = KindN(Kind),
2076+
% Next time return file + 2
2077+
WriteFile(N+2),
2078+
put({fake_activefile, Kind}, Fname(N+2)),
2079+
Fname(N);
2080+
File ->
2081+
File
2082+
end
2083+
end),
2084+
ReadFiles = lists:usort(bitcask:readable_files(Dir)),
2085+
meck:unload(),
2086+
?assertEqual([Fname(N)||N<-lists:seq(1,5)],
2087+
ReadFiles).
2088+
20522089
fold_test_() ->
20532090
{timeout, 60, fun fold_test2/0}.
20542091

0 commit comments

Comments
 (0)