summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Leeds <randall@apache.org>2012-03-18 16:00:24 -0700
committerRandall Leeds <randall@apache.org>2012-03-18 21:39:06 -0700
commit94e72e7fcc66f6740a3ec517a1bccf2cfa024cf7 (patch)
treedb987f1a3a2a3159827c70afb11e089e46e57c36
parenta38f59d1752ec611f0edc26e8f641a36b52f0c67 (diff)
downloadcouchdb-94e72e7fcc66f6740a3ec517a1bccf2cfa024cf7.tar.gz
don't delete view groups that fail to open
...but log a message instead. Fixes COUCHDB-1445.
-rw-r--r--src/couchdb/couch_util.erl7
-rw-r--r--src/couchdb/couch_view_group.erl17
-rwxr-xr-xtest/etap/200-view-group-no-db-leaks.t35
3 files changed, 50 insertions, 9 deletions
diff --git a/src/couchdb/couch_util.erl b/src/couchdb/couch_util.erl
index 6638b4724..f229760e1 100644
--- a/src/couchdb/couch_util.erl
+++ b/src/couchdb/couch_util.erl
@@ -17,7 +17,7 @@
-export([rand32/0, implode/2, collate/2, collate/3]).
-export([abs_pathname/1,abs_pathname/2, trim/1]).
-export([encodeBase64Url/1, decodeBase64Url/1]).
--export([validate_utf8/1, to_hex/1, parse_term/1, dict_find/3]).
+-export([validate_utf8/1, to_hex/1, from_hex/1, parse_term/1, dict_find/3]).
-export([get_nested_json_value/2, json_user_ctx/1]).
-export([proplist_apply_field/2, json_apply_field/2]).
-export([to_binary/1, to_integer/1, to_list/1, url_encode/1]).
@@ -136,6 +136,11 @@ to_hex([H|T]) ->
to_digit(N) when N < 10 -> $0 + N;
to_digit(N) -> $a + N-10.
+from_hex(Hex) when is_binary(Hex) ->
+ mochihex:to_bin(?b2l(Hex));
+from_hex(Hex) when is_list(Hex) ->
+ mochihex:to_bin(Hex).
+
parse_term(Bin) when is_binary(Bin) ->
parse_term(binary_to_list(Bin));
diff --git a/src/couchdb/couch_view_group.erl b/src/couchdb/couch_view_group.erl
index 97fc512d5..d5bd928b1 100644
--- a/src/couchdb/couch_view_group.erl
+++ b/src/couchdb/couch_view_group.erl
@@ -15,7 +15,8 @@
%% API
-export([start_link/1, request_group/2, request_group_info/1]).
--export([open_db_group/2, open_temp_group/5, design_doc_to_view_group/1,design_root/2]).
+-export([open_db_group/2, open_temp_group/5, design_doc_to_view_group/1]).
+-export([design_root/2, index_file_name/3]).
%% Exports for the compactor
-export([get_index_header_data/1]).
@@ -431,8 +432,11 @@ prepare_group({RootDir, DbName, #group{sig=Sig}=Group}, ForceReset)->
{ok, Db, reset_file(Db, Fd, DbName, Group)}
end
end;
- Error ->
- catch delete_index_file(RootDir, DbName, Sig),
+ {error, Reason} = Error ->
+ ?LOG_ERROR("Failed to open view file '~s': ~s", [
+ index_file_name(RootDir, DbName, Sig),
+ file:format_error(Reason)
+ ]),
Error
end;
Else ->
@@ -457,6 +461,10 @@ hex_sig(GroupSig) ->
design_root(RootDir, DbName) ->
RootDir ++ "/." ++ ?b2l(DbName) ++ "_design/".
+index_file_name(RootDir, DBName, Pid) when is_pid(Pid) ->
+ {ok, GroupInfo} = request_group_info(Pid),
+ GroupSig = couch_util:from_hex(couch_util:get_value(signature, GroupInfo)),
+ index_file_name(RootDir, DBName, GroupSig);
index_file_name(RootDir, DbName, GroupSig) ->
design_root(RootDir, DbName) ++ hex_sig(GroupSig) ++".view".
@@ -641,9 +649,6 @@ reset_file(Db, Fd, DbName, #group{sig=Sig,name=Name} = Group) ->
ok = couch_file:write_header(Fd, {Sig, nil}),
init_group(Db, Fd, reset_group(Group), nil).
-delete_index_file(RootDir, DbName, GroupSig) ->
- couch_file:delete(RootDir, index_file_name(RootDir, DbName, GroupSig)).
-
init_group(Db, Fd, #group{views=Views}=Group, nil) ->
init_group(Db, Fd, Group,
#index_header{seq=0, purge_seq=couch_db:get_purge_seq(Db),
diff --git a/test/etap/200-view-group-no-db-leaks.t b/test/etap/200-view-group-no-db-leaks.t
index 3aa26b6fa..b7ebeb3aa 100755
--- a/test/etap/200-view-group-no-db-leaks.t
+++ b/test/etap/200-view-group-no-db-leaks.t
@@ -54,7 +54,7 @@ ddoc_name() -> <<"foo">>.
main(_) ->
test_util:init_code_path(),
- etap:plan(28),
+ etap:plan(33),
case (catch test()) of
ok ->
etap:end_tests();
@@ -129,8 +129,39 @@ test() ->
etap:bail("old view group is not dead after ddoc update")
end,
+ NewViewGroup = couch_view:get_group_server(
+ test_db_name(), <<"_design/", (ddoc_name())/binary>>),
+ etap:is(is_pid(NewViewGroup), true, "got new view group pid"),
+ etap:is(is_process_alive(NewViewGroup), true, "new view group pid is alive"),
+
+ RootDir = couch_config:get("couchdb", "view_index_dir"),
+ IndexFileName = couch_view_group:index_file_name(
+ RootDir, test_db_name(), NewViewGroup),
+ ok = file:change_mode(IndexFileName, 8#00200),
+
+ MonRef1 = erlang:monitor(process, NewViewGroup),
+ exit(NewViewGroup, shutdown),
+ receive
+ {'DOWN', MonRef1, _, _, _} ->
+ etap:diag("new view group is dead after explicit shutdown")
+ after 5000 ->
+ etap:bail("old view group is not dead after explicit shutdown")
+ end,
+
+ ReadError = (catch couch_view:get_group_server(
+ test_db_name(), <<"_design/", (ddoc_name())/binary>>)),
+ etap:is(ReadError, {error, eacces},
+ "opening a view group requires file read access"),
+ etap:diag("checking that a view group is not deleted when open fails"),
+ ok = file:change_mode(IndexFileName, 8#00600),
+
+ NewViewGroup2 = couch_view:get_group_server(
+ test_db_name(), <<"_design/", (ddoc_name())/binary>>),
+ etap:is(is_pid(NewViewGroup2), true, "got new view group pid"),
+ etap:is(is_process_alive(NewViewGroup2), true, "new view group pid is alive"),
+
etap:diag("deleting database"),
- MonRef2 = erlang:monitor(process, NewViewGroup),
+ MonRef2 = erlang:monitor(process, NewViewGroup2),
ok = couch_server:delete(test_db_name(), []),
receive
{'DOWN', MonRef2, _, _, _} ->