From c823bd2e8b3990b5c55dde54705d6866ad1eda4f Mon Sep 17 00:00:00 2001 From: "Damien F. Katz" Date: Mon, 22 Dec 2008 20:35:50 +0000 Subject: Fix for leaked file handles when not explicitly closed, added file stats code for checking for leaked file handles, and some refactoring of the view api. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@728764 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/couch_db.erl | 2 +- src/couchdb/couch_db_updater.erl | 2 +- src/couchdb/couch_file.erl | 13 ++++++--- src/couchdb/couch_httpd_view.erl | 19 ++++++------ src/couchdb/couch_server.erl | 3 +- src/couchdb/couch_server_sup.erl | 6 ++++ src/couchdb/couch_view.erl | 62 ++++++++++++++++++++++------------------ 7 files changed, 61 insertions(+), 46 deletions(-) diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl index 6684156f..0e652b68 100644 --- a/src/couchdb/couch_db.erl +++ b/src/couchdb/couch_db.erl @@ -463,7 +463,7 @@ init({DbName, Filepath, Fd, Options}) -> gen_server:call(UpdaterPid, get_db). terminate(_Reason, Db) -> - exit(Db#db.update_pid, kill). + ok. handle_call({open_ref_counted_instance, OpenerPid}, _From, #db{fd=Fd}=Db) -> ok = couch_file:add_ref(Fd, OpenerPid), diff --git a/src/couchdb/couch_db_updater.erl b/src/couchdb/couch_db_updater.erl index 68c3a1fc..b4faefee 100644 --- a/src/couchdb/couch_db_updater.erl +++ b/src/couchdb/couch_db_updater.erl @@ -179,9 +179,9 @@ handle_cast({compact_done, CompactFilepath}, #db{filepath=Filepath}=Db) -> ?LOG_INFO("Compaction file still behind main file " "(update seq=~p. compact update seq=~p). Retrying.", [Db#db.update_seq, NewSeq]), + couch_file:close(NewFd), Pid = spawn_link(fun() -> start_copy_compact(Db) end), Db2 = Db#db{compactor_pid=Pid}, - couch_file:close(NewFd), {noreply, Db2} end. diff --git a/src/couchdb/couch_file.erl b/src/couchdb/couch_file.erl index 06156324..9e60eb09 100644 --- a/src/couchdb/couch_file.erl +++ b/src/couchdb/couch_file.erl @@ -36,6 +36,7 @@ open(Filepath, Options) -> case gen_server:start_link(couch_file, {Filepath, Options, self(), Ref = make_ref()}, []) of {ok, Fd} -> + couch_file_stats:track_file(Fd), {ok, Fd}; ignore -> % get the error @@ -164,7 +165,9 @@ sync(Fd) -> %% Returns: ok %%---------------------------------------------------------------------- close(Fd) -> - gen_server:cast(Fd, close). + Result = gen_server:cast(Fd, close), + catch unlink(Fd), + Result. close_maybe(Fd) -> gen_server:cast(Fd, {close_maybe, self()}). @@ -185,6 +188,7 @@ add_ref(Fd, Pid) -> num_refs(Fd) -> gen_server:call(Fd, num_refs). + write_header(Fd, Prefix, Data) -> TermBin = term_to_binary(Data), % the size of all the bytes written to the header, including the md5 signature (16 bytes) @@ -286,6 +290,7 @@ init_status_error(ReturnPid, Ref, Error) -> % server functions init({Filepath, Options, ReturnPid, Ref}) -> + process_flag(trap_exit, true), case lists:member(create, Options) of true -> filelib:ensure_dir(Filepath), @@ -325,8 +330,7 @@ init({Filepath, Options, ReturnPid, Ref}) -> end. -terminate(_Reason, Fd) -> - file:close(Fd), +terminate(_Reason, _Fd) -> ok. @@ -366,7 +370,6 @@ handle_call(num_refs, _From, Fd) -> {reply, length(Monitors), Fd}. - handle_cast(close, Fd) -> {stop,normal,Fd}; handle_cast({close_maybe, Pid}, Fd) -> @@ -388,6 +391,8 @@ handle_cast({drop_ref, Pid}, Fd) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. +handle_info({'EXIT', _Pid, Reason}, Fd) -> + {stop, Reason, Fd}; handle_info({'DOWN', MonitorRef, _Type, Pid, _Info}, Fd) -> {MonitorRef, _RefCount} = erase(Pid), maybe_close_async(Fd). diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl index 13842149..35203ced 100644 --- a/src/couchdb/couch_httpd_view.erl +++ b/src/couchdb/couch_httpd_view.erl @@ -26,21 +26,20 @@ design_doc_view(Req, Db, Id, ViewName, Keys) -> update = Update, reduce = Reduce } = QueryArgs = parse_view_query(Req, Keys), - case couch_view:get_map_view({couch_db:name(Db), - <<"_design/", Id/binary>>, ViewName, Update}) of + DesignId = <<"_design/", Id/binary>>, + case couch_view:get_map_view(Db, DesignId, ViewName, Update) of {ok, View} -> output_map_view(Req, View, Db, QueryArgs, Keys); {not_found, Reason} -> - case couch_view:get_reduce_view({couch_db:name(Db), - <<"_design/", Id/binary>>, ViewName}) of - {ok, View} -> + case couch_view:get_reduce_view(Db, DesignId, ViewName, Update) of + {ok, ReduceView} -> parse_view_query(Req, Keys, true), % just for validation case Reduce of false -> - {reduce, _N, _Lang, MapView} = View, + MapView = couch_view:extract_map_view(ReduceView), output_map_view(Req, MapView, Db, QueryArgs, Keys); _ -> - output_reduce_view(Req, View, QueryArgs, Keys) + output_reduce_view(Req, ReduceView, QueryArgs, Keys) end; _ -> throw({not_found, Reason}) @@ -72,11 +71,11 @@ handle_temp_view_req(#httpd{method='POST'}=Req, Db) -> Keys = proplists:get_value(<<"keys">>, Props, nil), case proplists:get_value(<<"reduce">>, Props, null) of null -> - {ok, View} = couch_view:get_map_view({temp, couch_db:name(Db), Language, MapSrc}), + {ok, View} = couch_view:get_temp_map_view(Db, Language, MapSrc), output_map_view(Req, View, Db, QueryArgs, Keys); RedSrc -> - {ok, View} = couch_view:get_reduce_view( - {temp, couch_db:name(Db), Language, MapSrc, RedSrc}), + {ok, View} = couch_view:get_temp_reduce_view(Db, Language, MapSrc, + RedSrc), output_reduce_view(Req, View, QueryArgs, Keys) end; diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl index 2a2f51e2..f0e85734 100644 --- a/src/couchdb/couch_server.erl +++ b/src/couchdb/couch_server.erl @@ -270,9 +270,8 @@ handle_call({create, DbName, Options}, _From, Server) -> Error -> {reply, Error, Server} end; -handle_call({delete, DbName, Options}, _From, Server) -> +handle_call({delete, DbName, _Options}, _From, Server) -> DbNameList = binary_to_list(DbName), - _UserCtx = proplists:get_value(user_ctx, Options, nil), case check_dbname(Server, DbNameList) of ok -> FullFilepath = get_full_filename(Server, DbNameList), diff --git a/src/couchdb/couch_server_sup.erl b/src/couchdb/couch_server_sup.erl index 9c74a1a3..f0157864 100644 --- a/src/couchdb/couch_server_sup.erl +++ b/src/couchdb/couch_server_sup.erl @@ -139,6 +139,12 @@ start_primary_services() -> brutal_kill, supervisor, [couch_server]}, + {couch_file_stats, + {couch_file_stats, start_link, []}, + permanent, + brutal_kill, + supervisor, + [couch_file_stats]}, {couch_db_update_event, {gen_event, start_link, [{local, couch_db_update}]}, permanent, diff --git a/src/couchdb/couch_view.erl b/src/couchdb/couch_view.erl index 84f327e3..665cb6f7 100644 --- a/src/couchdb/couch_view.erl +++ b/src/couchdb/couch_view.erl @@ -13,9 +13,11 @@ -module(couch_view). -behaviour(gen_server). --export([start_link/0,fold/4,fold/5,less_json/2,less_json_keys/2,expand_dups/2,detuple_kvs/2]). --export([init/1,terminate/2,handle_call/3,handle_cast/2,handle_info/2,code_change/3]). --export([get_reduce_view/1, get_map_view/1,get_row_count/1,reduce_to_count/1, fold_reduce/7]). +-export([start_link/0,fold/4,fold/5,less_json/2,less_json_keys/2,expand_dups/2, + detuple_kvs/2,init/1,terminate/2,handle_call/3,handle_cast/2,handle_info/2, + code_change/3,get_reduce_view/4,get_temp_reduce_view/4,get_temp_map_view/3, + get_map_view/4,get_row_count/1,reduce_to_count/1,fold_reduce/7, + extract_map_view/1]). -include("couch_db.hrl"). @@ -23,7 +25,8 @@ start_link() -> gen_server:start_link({local, couch_view}, couch_view, [], []). get_temp_updater(DbName, Type, MapSrc, RedSrc) -> - {ok, Pid} = gen_server:call(couch_view, {start_temp_updater, DbName, Type, MapSrc, RedSrc}), + {ok, Pid} = gen_server:call(couch_view, + {start_temp_updater, DbName, Type, MapSrc, RedSrc}), Pid. get_group_server(DbName, GroupId) -> @@ -34,21 +37,28 @@ get_group_server(DbName, GroupId) -> throw(Error) end. -get_updated_group(DbName, GroupId, Update) -> - couch_view_group:request_group(get_group_server(DbName, GroupId), seq_for_update(DbName, Update)). +get_group(Db, GroupId, Update) -> + couch_view_group:request_group( + get_group_server(couch_db:name(Db), GroupId), + if Update -> couch_db:get_update_seq(Db); true -> 0 end). -get_updated_group(temp, DbName, Type, MapSrc, RedSrc, Update) -> - couch_view_group:request_group(get_temp_updater(DbName, Type, MapSrc, RedSrc), seq_for_update(DbName, Update)). + +get_temp_group(Db, Type, MapSrc, RedSrc) -> + couch_view_group:request_group( + get_temp_updater(couch_db:name(Db), Type, MapSrc, RedSrc), + couch_db:get_update_seq(Db)). get_row_count(#view{btree=Bt}) -> {ok, {Count, _Reds}} = couch_btree:full_reduce(Bt), {ok, Count}. -get_reduce_view({temp, DbName, Type, MapSrc, RedSrc}) -> - {ok, #group{views=[View]}} = get_updated_group(temp, DbName, Type, MapSrc, RedSrc, true), - {ok, {temp_reduce, View}}; -get_reduce_view({DbName, GroupId, Name}) -> - case get_updated_group(DbName, GroupId, true) of +get_temp_reduce_view(Db, Type, MapSrc, RedSrc) -> + {ok, #group{views=[View]}} = get_temp_group(Db, Type, MapSrc, RedSrc), + {ok, {temp_reduce, View}}. + + +get_reduce_view(Db, GroupId, Name, Update) -> + case get_group(Db, GroupId, Update) of {ok, #group{views=Views,def_lang=Lang}} -> get_reduce_view0(Name, Lang, Views); Error -> @@ -63,6 +73,9 @@ get_reduce_view0(Name, Lang, [#view{reduce_funs=RedFuns}=View|Rest]) -> N -> {ok, {reduce, N, Lang, View}} end. +extract_map_view({reduce, _N, _Lang, View}) -> + View. + detuple_kvs([], Acc) -> lists:reverse(Acc); detuple_kvs([KV | Rest], Acc) -> @@ -113,21 +126,14 @@ get_key_pos(Key, [{Key1,_Value}|_], N) when Key == Key1 -> N + 1; get_key_pos(Key, [_|Rest], N) -> get_key_pos(Key, Rest, N+1). - -seq_for_update(DbName, Update) -> - case Update of - true -> - {ok, #db{update_seq=CurrentSeq}} = couch_db:open(DbName, []), - CurrentSeq; - _Else -> - 0 - end. - -get_map_view({temp, DbName, Type, Src}) -> - {ok, #group{views=[View]}} = get_updated_group(temp, DbName, Type, Src, [], true), - {ok, View}; -get_map_view({DbName, GroupId, Name, Update}) -> - case get_updated_group(DbName, GroupId, Update) of + + +get_temp_map_view(Db, Type, Src) -> + {ok, #group{views=[View]}} = get_temp_group(Db, Type, Src, []), + {ok, View}. + +get_map_view(Db, GroupId, Name, Update) -> + case get_group(Db, GroupId, Update) of {ok, #group{views=Views}} -> get_map_view0(Name, Views); Error -> -- cgit v1.2.3