Skip to content

Commit d7f54ce

Browse files
committed
Move view index files to .view_deleted when db is deleted
Bugzid 86318
1 parent b2397c5 commit d7f54ce

File tree

3 files changed

+91
-32
lines changed

3 files changed

+91
-32
lines changed

src/couch/src/couch_file.erl

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
-export([append_raw_chunk/2, assemble_file_chunk/1, assemble_file_chunk/2]).
4444
-export([append_term/2, append_term/3, append_term_md5/2, append_term_md5/3]).
4545
-export([write_header/2, read_header/1]).
46-
-export([delete/2, delete/3, nuke_dir/2, init_delete_dir/1]).
46+
-export([delete/2, delete/3, nuke_dir/2, nuke_dir/3]).
47+
-export([init_delete_dir/1, init_recovery_dir/1]).
4748
-export([msec_since_last_read/1]).
4849

4950
% gen_server callbacks
@@ -265,6 +266,18 @@ rename_file(Original) ->
265266
Else -> Else
266267
end.
267268

269+
rename_dir(RootDelDir, Original, DbName) ->
270+
DbDir = binary_to_list(DbName) ++ "_design",
271+
RenamedIndexDir = filename:join(
272+
[RootDelDir, ".recovery", DbDir]
273+
),
274+
Now = calendar:local_time(),
275+
filelib:ensure_dir(RenamedIndexDir),
276+
case file:rename(Original, RenamedIndexDir) of
277+
ok -> file:change_time(RenamedIndexDir, Now);
278+
Else -> Else
279+
end.
280+
268281
deleted_filename(Original) ->
269282
{{Y, Mon, D}, {H, Min, S}} = calendar:universal_time(),
270283
Suffix = lists:flatten(
@@ -273,14 +286,18 @@ deleted_filename(Original) ->
273286
++ filename:extension(Original), [Y, Mon, D, H, Min, S])),
274287
filename:rootname(Original) ++ Suffix.
275288

276-
nuke_dir(RootDelDir, Dir) ->
289+
nuke_dir(RootDir, Dir) ->
290+
nuke_dir(RootDir, Dir, []).
291+
nuke_dir(RootDelDir, Dir, Options) ->
277292
EnableRecovery = config:get_boolean("couchdb",
278293
"enable_database_recovery", false),
294+
Context = couch_util:get_value(context, Options, compaction),
279295
case EnableRecovery of
280-
true ->
281-
rename_file(Dir);
282-
false ->
283-
delete_dir(RootDelDir, Dir)
296+
true when Context == delete ->
297+
DbName = couch_util:get_value(db_name, Options),
298+
rename_dir(RootDelDir, Dir, DbName);
299+
true -> rename_file(Dir);
300+
false -> delete_dir(RootDelDir, Dir)
284301
end.
285302

286303
delete_dir(RootDelDir, Dir) ->
@@ -318,6 +335,9 @@ init_delete_dir(RootDir) ->
318335
end),
319336
ok.
320337

338+
init_recovery_dir(RootDir) ->
339+
Dir = filename:join(RootDir, ".recovery"),
340+
filelib:ensure_dir(filename:join(Dir, "foo")).
321341

322342
read_header(Fd) ->
323343
case ioq:call(Fd, find_header, erlang:get(io_priority)) of
@@ -762,11 +782,25 @@ make_filename_fixtures(DbNames) ->
762782
"shards/00000000-1fffffff/~s.1458336317.couch",
763783
".shards/00000000-1fffffff/~s.1458336317_design",
764784
".shards/00000000-1fffffff/~s.1458336317_design"
765-
"/mrview/3133e28517e89a3e11435dd5ac4ad85a.view"
785+
"/mrview/3133e28517e89a3e11435dd5ac4ad85a.view",
786+
".recovery/1499329402/shards/00000000-1fffffff/~s.1499329402_design",
787+
".recovery/1499329402/shards/00000000-1fffffff/~s.1499329402_design"
788+
"/mrview/8fabddcb28f501d6764afd7def3bd352.view"
766789
],
767790
lists:flatmap(fun(DbName) ->
768791
lists:map(fun(Format) ->
769-
filename:join("/srv/data", io_lib:format(Format, [DbName]))
792+
% Count how many times we need to specify the database name
793+
% by splitting on the ~s formatter.
794+
ArgCount = erlang:length(binary:split(
795+
list_to_binary(Format),
796+
<<"~s">>,
797+
[global])
798+
) - 1,
799+
Args = case ArgCount of
800+
1 -> [DbName];
801+
2 -> [DbName, DbName]
802+
end,
803+
filename:join("/srv/data/", io_lib:format(Format, Args))
770804
end, Formats)
771805
end, DbNames).
772806

src/couch/test/couch_file_tests.erl

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -419,32 +419,44 @@ nuke_dir_test_() ->
419419
RootDir = filename:dirname(File0),
420420
BaseName = filename:basename(File0),
421421
Seed = crypto:rand_uniform(1000000000, 9999999999),
422-
DDocDir = io_lib:format("db.~b_design", [Seed]),
422+
DBName0 = io_lib:format("db.~b", [Seed]),
423+
DBName = iolist_to_binary(DBName0),
424+
DDocDir = io_lib:format("~s_design", [DBName0]),
423425
ViewDir = filename:join([RootDir, DDocDir]),
424426
file:make_dir(ViewDir),
425427
File = filename:join([ViewDir, BaseName]),
426428
file:rename(File0, File),
427429
ok = couch_file:init_delete_dir(RootDir),
428430
ok = file:write_file(File, <<>>),
429-
{RootDir, ViewDir}
431+
{RootDir, ViewDir, DBName}
430432
end,
431-
fun({RootDir, ViewDir}) ->
433+
fun({RootDir, ViewDir, _DBName}) ->
432434
meck:unload(config),
433435
remove_dir(ViewDir),
434436
Ext = filename:extension(ViewDir),
435437
case filelib:wildcard(RootDir ++ "/*.deleted" ++ Ext) of
436438
[DelDir] -> remove_dir(DelDir);
437439
_ -> ok
438-
end
440+
end,
441+
RecDirPaths = RootDir ++ "/.recovery" ++ "/*_design",
442+
[remove_dir(Dir) || Dir <- filelib:wildcard(RecDirPaths)]
439443
end,
440444
[
441445
fun(Cfg) ->
442-
{"enable_database_recovery = false",
443-
make_rename_dir_test_case(Cfg, false)}
446+
{"enable_database_recovery = false, context = delete",
447+
make_rename_dir_test_case(Cfg, false, delete)}
448+
end,
449+
fun(Cfg) ->
450+
{"enable_database_recovery = false, context = compaction",
451+
make_rename_dir_test_case(Cfg, false, compaction)}
452+
end,
453+
fun(Cfg) ->
454+
{"enable_database_recovery = true, context = delete",
455+
make_rename_dir_test_case(Cfg, true, delete)}
444456
end,
445457
fun(Cfg) ->
446-
{"enable_database_recovery = true",
447-
make_rename_dir_test_case(Cfg, true)}
458+
{"enable_database_recovery = true, context = compaction",
459+
make_rename_dir_test_case(Cfg, true, compaction)}
448460
end,
449461
fun(Cfg) ->
450462
{"delete_after_rename = true",
@@ -459,24 +471,34 @@ nuke_dir_test_() ->
459471
}.
460472

461473

462-
make_rename_dir_test_case({RootDir, ViewDir}, EnableRecovery) ->
474+
make_rename_dir_test_case({RootDir, ViewDir, DBName}, EnableRecovery, Context) ->
463475
meck:expect(config, get_boolean, fun
464476
("couchdb", "enable_database_recovery", _) -> EnableRecovery;
465477
("couchdb", "delete_after_rename", _) -> true
466478
end),
467479
DirExistsBefore = filelib:is_dir(ViewDir),
468-
couch_file:nuke_dir(RootDir, ViewDir),
480+
481+
couch_file:nuke_dir(
482+
RootDir,
483+
ViewDir,
484+
[{db_name, DBName}, {context, Context}]
485+
),
469486
DirExistsAfter = filelib:is_dir(ViewDir),
470-
Ext = filename:extension(ViewDir),
471-
RenamedDirs = filelib:wildcard(RootDir ++ "/*.deleted" ++ Ext),
487+
RenamedDirs = if Context =:= delete ->
488+
filelib:wildcard(RootDir ++ "/.recovery" ++ "/*_design");
489+
true ->
490+
Ext = filename:extension(ViewDir),
491+
filelib:wildcard(RootDir ++ "/*.deleted" ++ Ext)
492+
end,
493+
472494
ExpectRenamedCount = if EnableRecovery -> 1; true -> 0 end,
473495
[
474496
?_assert(DirExistsBefore),
475497
?_assertNot(DirExistsAfter),
476498
?_assertEqual(ExpectRenamedCount, length(RenamedDirs))
477499
].
478500

479-
make_delete_dir_test_case({RootDir, ViewDir}, DeleteAfterRename) ->
501+
make_delete_dir_test_case({RootDir, ViewDir, _DBName}, DeleteAfterRename) ->
480502
meck:expect(config, get_boolean, fun
481503
("couchdb", "enable_database_recovery", _) -> false;
482504
("couchdb", "delete_after_rename", _) -> DeleteAfterRename

src/couch_index/src/couch_index_server.erl

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ init([]) ->
129129
couch_event:link_listener(?MODULE, handle_db_event, nil, [all_dbs]),
130130
RootDir = couch_index_util:root_dir(),
131131
couch_file:init_delete_dir(RootDir),
132+
couch_file:init_recovery_dir(RootDir),
132133
{ok, #st{root_dir=RootDir}}.
133134

134135

@@ -161,14 +162,9 @@ handle_call({async_error, {DbName, _DDocId, Sig}, Error}, _From, State) ->
161162
[gen_server:reply(From, Error) || From <- Waiters],
162163
ets:delete(?BY_SIG, {DbName, Sig}),
163164
{reply, ok, State};
164-
handle_call({reset_indexes, DbName}, _From, State) ->
165-
reset_indexes(DbName, State#st.root_dir),
165+
handle_call({reset_indexes, DbName, Options}, _From, State) ->
166+
reset_indexes(DbName, State#st.root_dir, Options),
166167
{reply, ok, State}.
167-
168-
169-
handle_cast({reset_indexes, DbName}, State) ->
170-
reset_indexes(DbName, State#st.root_dir),
171-
{noreply, State};
172168
handle_cast({add_to_ets, [Pid, DbName, DDocId, Sig]}, State) ->
173169
% check if Pid still exists
174170
case ets:lookup(?BY_PID, Pid) of
@@ -179,8 +175,14 @@ handle_cast({add_to_ets, [Pid, DbName, DDocId, Sig]}, State) ->
179175
{noreply, State};
180176
handle_cast({rem_from_ets, [DbName, DDocId, Sig]}, State) ->
181177
ets:delete_object(?BY_DB, {DbName, {DDocId, Sig}}),
178+
{noreply, State};
179+
180+
181+
handle_cast({reset_indexes, DbName, Options}, State) ->
182+
reset_indexes(DbName, State#st.root_dir, Options),
182183
{noreply, State}.
183184

185+
184186
handle_info({'EXIT', Pid, Reason}, Server) ->
185187
case ets:lookup(?BY_PID, Pid) of
186188
[{Pid, {DbName, Sig}}] ->
@@ -237,7 +239,7 @@ new_index({Mod, IdxState, DbName, Sig}) ->
237239
end.
238240

239241

240-
reset_indexes(DbName, Root) ->
242+
reset_indexes(DbName, Root, Options) ->
241243
% shutdown all the updaters and clear the files, the db got changed
242244
SigDDocIds = lists:foldl(fun({_, {DDocId, Sig}}, DDict) ->
243245
dict:append(Sig, DDocId, DDict)
@@ -251,7 +253,8 @@ reset_indexes(DbName, Root) ->
251253
end,
252254
lists:foreach(Fun, dict:to_list(SigDDocIds)),
253255
Path = couch_index_util:index_dir("", DbName),
254-
couch_file:nuke_dir(Root, Path).
256+
Options1 = [{db_name, DbName} | Options],
257+
couch_file:nuke_dir(Root, Path, Options1).
255258

256259

257260
add_to_ets(DbName, Sig, DDocId, Pid) ->
@@ -269,10 +272,10 @@ rem_from_ets(DbName, Sig, DDocIds, Pid) ->
269272

270273

271274
handle_db_event(DbName, created, St) ->
272-
gen_server:cast(?MODULE, {reset_indexes, DbName}),
275+
gen_server:cast(?MODULE, {reset_indexes, DbName, []}),
273276
{ok, St};
274277
handle_db_event(DbName, deleted, St) ->
275-
gen_server:cast(?MODULE, {reset_indexes, DbName}),
278+
gen_server:cast(?MODULE, {reset_indexes, DbName, [{context, delete}]}),
276279
{ok, St};
277280
handle_db_event(<<"shards/", _/binary>> = DbName, {ddoc_updated,
278281
DDocId}, St) ->

0 commit comments

Comments
 (0)