You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2014/08/05 18:47:16 UTC

[16/34] couch commit: updated refs/heads/windsor-merge-209 to f2368da

Allow graceful reloading of OS processes

This patch adds a reload/0 command to couch_proc_manager that will cause
all OS processes to be reclaimed at the next earliest convenience (i.e.,
when the current client is done with the process).  It also adds a
get_stale_proc_count/0 function to report the number of processes that
have yet to be reclaimied.

BugzID: 19529


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/1f5673fa
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/1f5673fa
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/1f5673fa

Branch: refs/heads/windsor-merge-209
Commit: 1f5673facc451087edf7622ce337a40a7cb5e996
Parents: 974d85e
Author: Adam Kocoloski <ad...@cloudant.com>
Authored: Mon Jun 3 12:26:35 2013 -0400
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 12:03:14 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 62 +++++++++++++++++++++++++++++++++--------
 1 file changed, 51 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1f5673fa/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index e2a4657..8288b13 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -17,7 +17,13 @@
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, 
     code_change/3]).
 
--export([start_link/0, get_proc_count/0, new_proc/1]).
+-export([
+    start_link/0,
+    get_proc_count/0,
+    get_stale_proc_count/0,
+    new_proc/1,
+    reload/0
+]).
 
 % config_listener api
 -export([handle_config_change/5]).
@@ -28,7 +34,8 @@
     tab,
     config,
     proc_counts,
-    waiting
+    waiting,
+    threshold_ts
 }).
 
 -record(client, {
@@ -46,7 +53,8 @@
     ddoc_keys = [],
     prompt_fun,
     set_timeout_fun,
-    stop_fun
+    stop_fun,
+    t0 = os:timestamp()
 }).
 
 start_link() ->
@@ -55,6 +63,12 @@ start_link() ->
 get_proc_count() ->
     gen_server:call(?MODULE, get_proc_count).
 
+get_stale_proc_count() ->
+    gen_server:call(?MODULE, get_stale_proc_count).
+
+reload() ->
+    gen_server:call(?MODULE, bump_threshold_ts).
+
 init([]) ->
     process_flag(trap_exit, true),
     ok = config:listen_for_changes(?MODULE, nil),
@@ -72,6 +86,11 @@ handle_call(get_table, _From, State) ->
 handle_call(get_proc_count, _From, State) ->
     {reply, ets:info(State#state.tab, size), State};
 
+handle_call(get_stale_proc_count, _From, State) ->
+    #state{tab = Tab, threshold_ts = T0} = State,
+    MatchSpec = [{#proc_int{t0='$1', _='_'}, [{'<', '$1', T0}], [true]}],
+    {reply, ets:select_count(Tab, MatchSpec), State};
+
 handle_call({get_proc, #doc{body={Props}}=DDoc, DDocKey}, From, State) ->
     {ClientPid, _} = From,
     Lang = couch_util:to_binary(
@@ -112,6 +131,16 @@ handle_call({ret_proc, #proc{client=Ref, lang=Lang0} = Proc}, _From, State) ->
     % table before the insert.  Don't know which approach is cheaper.
     {reply, true, return_proc(State, Proc#proc{lang=Lang})};
 
+handle_call(bump_threshold_ts, _From, #state{tab = Tab} = State) ->
+    FoldFun = fun(#proc_int{client = nil, pid = Pid}, _) ->
+        gen_server:cast(Pid, stop),
+        ets:delete(Tab, Pid);
+    (_, _) ->
+        ok
+    end,
+    ets:foldl(FoldFun, nil, Tab),
+    {reply, ok, State#state{threshold_ts = os:timestamp()}};
+
 handle_call(_Call, _From, State) ->
     {reply, ignored, State}.
 
@@ -199,11 +228,17 @@ terminate(_Reason, #state{tab=Tab}) ->
     ets:foldl(fun(#proc_int{pid=P}, _) -> couch_util:shutdown_sync(P) end, 0, Tab),
     ok.
 
-code_change(_OldVsn, #state{tab = Tab} = State, _Extra) ->
-    NewTab = ets:new(procs, [ordered_set, {keypos, #proc_int.pid}]),
-    true = ets:insert(NewTab, ets:tab2list(Tab)),
-    true = ets:delete(Tab),
-    {ok, State#state{tab = NewTab}}.
+code_change(_OldVsn, {state, Tab}, _Extra) ->
+    State = #state{tab = Tab, threshold_ts = {0,0,0}},
+    ProcInts = lists:map(
+        fun(#proc{} = P) ->
+            setelement(1, erlang:append_element(P, os:timestamp()), proc_int)
+        end,
+        ets:tab2list(Tab)
+    ),
+    ets:delete_all_objects(Tab),
+    ets:insert(Tab, ProcInts),
+    {ok, State}.
 
 handle_config_change("query_server_config", _, _, _, _) ->
     gen_server:cast(?MODULE, reload_config),
@@ -353,13 +388,18 @@ return_proc(#state{} = State, #proc{} = Proc) ->
             ok
     end;
 return_proc(#state{} = State, #proc_int{} = ProcInt) ->
-    #state{tab = Tab, waiting = Waiting} = State,
+    #state{tab = Tab, waiting = Waiting, threshold_ts = T0} = State,
     #proc_int{pid = Pid, lang = Lang} = ProcInt,
     case is_process_alive(Pid) of true ->
         case get_waiting_client(Waiting, Lang) of
             nil ->
-                gen_server:cast(Pid, garbage_collect),
-                ets:insert(Tab, ProcInt#proc_int{client=nil}),
+                if ProcInt#proc_int.t0 < T0 ->
+                    gen_server:cast(Pid, stop),
+                    ets:delete(Tab, Pid);
+                true ->
+                    gen_server:cast(Pid, garbage_collect),
+                    ets:insert(Tab, ProcInt#proc_int{client=nil})
+                end,
                 State;
             #client{}=Client ->
                 From = Client#client.from,