diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/couch/src/couch_os_process.erl | 37 | ||||
-rw-r--r-- | apps/couch/src/couch_proc_manager.erl | 31 |
2 files changed, 42 insertions, 26 deletions
diff --git a/apps/couch/src/couch_os_process.erl b/apps/couch/src/couch_os_process.erl index 2a6d92a7..0c6f284f 100644 --- a/apps/couch/src/couch_os_process.erl +++ b/apps/couch/src/couch_os_process.erl @@ -27,7 +27,8 @@ port, writer, reader, - timeout=5000 + timeout=5000, + idle }). start_link(Command) -> @@ -104,12 +105,15 @@ readjson(#os_proc{} = OsProc) -> % gen_server API init([Command, Options, PortOptions]) -> + V = couch_config:get("query_server_config", "os_process_idle_limit", "300"), + IdleLimit = list_to_integer(V) * 1000, Spawnkiller = filename:join([code:priv_dir(couch), "couchspawnkillable.sh"]), BaseProc = #os_proc{ command=Command, port=open_port({spawn, Spawnkiller ++ " " ++ Command}, PortOptions), writer=fun writejson/2, - reader=fun readjson/1 + reader=fun readjson/1, + idle=IdleLimit }, KillCmd = readline(BaseProc), Pid = self(), @@ -131,32 +135,32 @@ init([Command, Options, PortOptions]) -> Proc#os_proc{timeout=TimeOut} end end, BaseProc, Options), - {ok, OsProc}. + {ok, OsProc, IdleLimit}. terminate(_Reason, #os_proc{port=Port}) -> catch port_close(Port), ok. -handle_call({set_timeout, TimeOut}, _From, OsProc) -> - {reply, ok, OsProc#os_proc{timeout=TimeOut}}; -handle_call({prompt, Data}, _From, OsProc) -> +handle_call({set_timeout, TimeOut}, _From, #os_proc{idle=Idle}=OsProc) -> + {reply, ok, OsProc#os_proc{timeout=TimeOut}, Idle}; +handle_call({prompt, Data}, _From, #os_proc{idle=Idle}=OsProc) -> #os_proc{writer=Writer, reader=Reader} = OsProc, try Writer(OsProc, Data), - {reply, {ok, Reader(OsProc)}, OsProc} + {reply, {ok, Reader(OsProc)}, OsProc, Idle} catch throw:{error, OsError} -> - {reply, OsError, OsProc}; + {reply, OsError, OsProc, Idle}; throw:{fatal, OsError} -> {stop, normal, OsError, OsProc}; throw:OtherError -> {stop, normal, OtherError, OsProc} end. -handle_cast({send, Data}, #os_proc{writer=Writer}=OsProc) -> +handle_cast({send, Data}, #os_proc{writer=Writer, idle=Idle}=OsProc) -> try Writer(OsProc, Data), - {noreply, OsProc} + {noreply, OsProc, Idle} catch throw:OsError -> ?LOG_ERROR("Failed sending data: ~p -> ~p", [Data, OsError]), @@ -164,16 +168,23 @@ handle_cast({send, Data}, #os_proc{writer=Writer}=OsProc) -> end; handle_cast(stop, OsProc) -> {stop, normal, OsProc}; -handle_cast(Msg, OsProc) -> +handle_cast(Msg, #os_proc{idle=Idle}=OsProc) -> ?LOG_DEBUG("OS Proc: Unknown cast: ~p", [Msg]), - {noreply, OsProc}. + {noreply, OsProc, Idle}. +handle_info(timeout, #os_proc{idle=Idle}=OsProc) -> + gen_server:cast(couch_proc_manager, {os_proc_idle, self()}), + erlang:garbage_collect(), + {noreply, OsProc, Idle}; handle_info({Port, {exit_status, 0}}, #os_proc{port=Port}=OsProc) -> ?LOG_INFO("OS Process terminated normally", []), {stop, normal, OsProc}; handle_info({Port, {exit_status, Status}}, #os_proc{port=Port}=OsProc) -> ?LOG_ERROR("OS Process died with status: ~p", [Status]), - {stop, {exit_status, Status}, OsProc}. + {stop, {exit_status, Status}, OsProc}; +handle_info(Msg, #os_proc{idle=Idle}=OsProc) -> + ?LOG_DEBUG("OS Proc: Unknown info: ~p", [Msg]), + {noreply, OsProc, Idle}. code_change(_OldVsn, State, _Extra) -> {ok, State}. diff --git a/apps/couch/src/couch_proc_manager.erl b/apps/couch/src/couch_proc_manager.erl index 0f35422b..568608bc 100644 --- a/apps/couch/src/couch_proc_manager.erl +++ b/apps/couch/src/couch_proc_manager.erl @@ -67,13 +67,29 @@ handle_call({ret_proc, #proc{client=Ref, pid=Pid} = Proc}, _From, State) -> % #proc{} from our own table, so the alternative is to do a lookup in the % table before the insert. Don't know which approach is cheaper. case is_process_alive(Pid) of true -> - maybe_reuse_proc(State#state.tab, Proc); + ets:insert(State#state.tab, Proc); false -> ok end, {reply, true, State}; handle_call(_Call, _From, State) -> {reply, ignored, State}. +handle_cast({os_proc_idle, Pid}, #state{tab=Tab}=State) -> + Limit = couch_config:get("query_server_config", "os_process_soft_limit", "100"), + case ets:info(Tab, size) > list_to_integer(Limit) of + true -> + ets:delete(Tab, Pid), + case is_process_alive(Pid) of + true -> + unlink(Pid), + gen_server:cast(Pid, stop); + _ -> + ok + end; + _ -> + ok + end, + {noreply, State}; handle_cast(_Msg, State) -> {noreply, State}. @@ -95,7 +111,7 @@ handle_info({'DOWN', Ref, _, _, _Reason}, State) -> ok; [#proc{pid = Pid} = Proc] -> case is_process_alive(Pid) of true -> - maybe_reuse_proc(State#state.tab, Proc); + ets:insert(State#state.tab, Proc); false -> ok end end, {noreply, State}; @@ -110,17 +126,6 @@ terminate(_Reason, #state{tab=Tab}) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -maybe_reuse_proc(Tab, #proc{pid = Pid} = Proc) -> - Limit = couch_config:get("query_server_config", "os_process_soft_limit", "100"), - case ets:info(Tab, size) > list_to_integer(Limit) of - true -> - ets:delete(Tab, Pid), - unlink(Pid), - exit(Pid, kill); - false -> - garbage_collect(Pid), - ets:insert(Tab, Proc#proc{client=nil}) - end. get_procs(Tab, Lang) when is_binary(Lang) -> get_procs(Tab, binary_to_list(Lang)); |