diff options
author | Randall Leeds <randall@apache.org> | 2012-03-18 16:00:24 -0700 |
---|---|---|
committer | Randall Leeds <randall@apache.org> | 2012-03-18 21:39:06 -0700 |
commit | 94e72e7fcc66f6740a3ec517a1bccf2cfa024cf7 (patch) | |
tree | db987f1a3a2a3159827c70afb11e089e46e57c36 | |
parent | a38f59d1752ec611f0edc26e8f641a36b52f0c67 (diff) | |
download | couchdb-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.erl | 7 | ||||
-rw-r--r-- | src/couchdb/couch_view_group.erl | 17 | ||||
-rwxr-xr-x | test/etap/200-view-group-no-db-leaks.t | 35 |
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, _, _, _} -> |