Extend `enable_database_recovery` behaviour to views directories
If config option `enable_database_recovery` set to true then during database
deletion all its ddoc's directories that hold view files will be renamed
in the same fashion as database file, i.e. with added timestamp and
suffix "deleted"
diff --git a/src/couch_file.erl b/src/couch_file.erl
index d2c28d4..8aa4a50 100644
--- a/src/couch_file.erl
+++ b/src/couch_file.erl
@@ -264,6 +264,18 @@
filename:rootname(Original) ++ Suffix.
nuke_dir(RootDelDir, Dir) ->
+ EnableRecovery = config:get_boolean("couchdb",
+ "enable_database_recovery", false),
+ case EnableRecovery of
+ true ->
+ rename_file(Dir);
+ false ->
+ delete_dir(RootDelDir, Dir)
+ end.
+
+delete_dir(RootDelDir, Dir) ->
+ DeleteAfterRename = config:get_boolean("couchdb",
+ "delete_after_rename", true),
FoldFun = fun(File) ->
Path = Dir ++ "/" ++ File,
case filelib:is_dir(Path) of
@@ -271,7 +283,7 @@
ok = nuke_dir(RootDelDir, Path),
file:del_dir(Path);
false ->
- delete(RootDelDir, Path, false)
+ delete_file(RootDelDir, Path, false, DeleteAfterRename)
end
end,
case file:list_dir(Dir) of
diff --git a/test/couch_file_tests.erl b/test/couch_file_tests.erl
index 8edbaac..62f5844 100644
--- a/test/couch_file_tests.erl
+++ b/test/couch_file_tests.erl
@@ -346,3 +346,95 @@
?_assertNot(FileExistsAfter),
?_assertEqual(ExpectRenamedCount, length(RenamedFiles))
].
+
+
+nuke_dir_test_() ->
+ {
+ "Nuke directory tests",
+ {
+ foreach,
+ fun() ->
+ meck:new(config, [passthrough]),
+ File0 = ?tempfile() ++ ".couch",
+ RootDir = filename:dirname(File0),
+ BaseName = filename:basename(File0),
+ Seed = crypto:rand_uniform(1000000000, 9999999999),
+ DDocDir = io_lib:format("db.~b_design", [Seed]),
+ ViewDir = filename:join([RootDir, DDocDir]),
+ file:make_dir(ViewDir),
+ File = filename:join([ViewDir, BaseName]),
+ file:rename(File0, File),
+ ok = couch_file:init_delete_dir(RootDir),
+ ok = file:write_file(File, <<>>),
+ {RootDir, ViewDir}
+ end,
+ fun({RootDir, ViewDir}) ->
+ meck:unload(config),
+ remove_dir(ViewDir),
+ Ext = filename:extension(ViewDir),
+ case filelib:wildcard(RootDir ++ "/*.deleted" ++ Ext) of
+ [DelDir] -> remove_dir(DelDir);
+ _ -> ok
+ end
+ end,
+ [
+ fun(Cfg) ->
+ {"enable_database_recovery = false",
+ make_rename_dir_test_case(Cfg, false)}
+ end,
+ fun(Cfg) ->
+ {"enable_database_recovery = true",
+ make_rename_dir_test_case(Cfg, true)}
+ end,
+ fun(Cfg) ->
+ {"delete_after_rename = true",
+ make_delete_dir_test_case(Cfg, true)}
+ end,
+ fun(Cfg) ->
+ {"delete_after_rename = false",
+ make_delete_dir_test_case(Cfg, false)}
+ end
+ ]
+ }
+ }.
+
+
+make_rename_dir_test_case({RootDir, ViewDir}, EnableRecovery) ->
+ meck:expect(config, get_boolean, fun
+ ("couchdb", "enable_database_recovery", _) -> EnableRecovery;
+ ("couchdb", "delete_after_rename", _) -> true
+ end),
+ DirExistsBefore = filelib:is_dir(ViewDir),
+ couch_file:nuke_dir(RootDir, ViewDir),
+ DirExistsAfter = filelib:is_dir(ViewDir),
+ Ext = filename:extension(ViewDir),
+ RenamedDirs = filelib:wildcard(RootDir ++ "/*.deleted" ++ Ext),
+ ExpectRenamedCount = if EnableRecovery -> 1; true -> 0 end,
+ [
+ ?_assert(DirExistsBefore),
+ ?_assertNot(DirExistsAfter),
+ ?_assertEqual(ExpectRenamedCount, length(RenamedDirs))
+ ].
+
+make_delete_dir_test_case({RootDir, ViewDir}, DeleteAfterRename) ->
+ meck:expect(config, get_boolean, fun
+ ("couchdb", "enable_database_recovery", _) -> false;
+ ("couchdb", "delete_after_rename", _) -> DeleteAfterRename
+ end),
+ DirExistsBefore = filelib:is_dir(ViewDir),
+ couch_file:nuke_dir(RootDir, ViewDir),
+ DirExistsAfter = filelib:is_dir(ViewDir),
+ Ext = filename:extension(ViewDir),
+ RenamedDirs = filelib:wildcard(RootDir ++ "/*.deleted" ++ Ext),
+ RenamedFiles = filelib:wildcard(RootDir ++ "/.delete/*"),
+ ExpectRenamedCount = if DeleteAfterRename -> 0; true -> 1 end,
+ [
+ ?_assert(DirExistsBefore),
+ ?_assertNot(DirExistsAfter),
+ ?_assertEqual(0, length(RenamedDirs)),
+ ?_assertEqual(ExpectRenamedCount, length(RenamedFiles))
+ ].
+
+remove_dir(Dir) ->
+ [file:delete(File) || File <- filelib:wildcard(filename:join([Dir, "*"]))],
+ file:del_dir(Dir).