diff options
author | Paul J. Davis <paul.joseph.davis@gmail.com> | 2011-12-19 13:39:49 -0600 |
---|---|---|
committer | Robert Newson <robert.newson@cloudant.com> | 2012-11-14 17:27:34 +0000 |
commit | c15440cd74b04ba88f9af0a59f9505478d314d38 (patch) | |
tree | d7c87e25efc5f85fc1a7b4b6375cff8a79e43d62 /apps/couch | |
parent | 017ba1c626e565d3b70c90ca92496d2602e4a909 (diff) |
Avoid scanning the entire table of procs
For large numbers of os processes its possible that we have a slowdown
when requesting a new process. The old code matches all possible
processes out of the table to find an appropriate candidate.
We avoid the issue by using ets:select_reverse to also prefer keeping
newer processes and releasing longer lived processes. Length of life is
based on the implicit sorting of pids having newer pids sorting larger.
Diffstat (limited to 'apps/couch')
-rw-r--r-- | apps/couch/src/couch_proc_manager.erl | 87 |
1 files changed, 64 insertions, 23 deletions
diff --git a/apps/couch/src/couch_proc_manager.erl b/apps/couch/src/couch_proc_manager.erl index 6c2d339f..d3072c71 100644 --- a/apps/couch/src/couch_proc_manager.erl +++ b/apps/couch/src/couch_proc_manager.erl @@ -17,7 +17,7 @@ get_proc_count() -> init([]) -> process_flag(trap_exit, true), - {ok, #state{tab = ets:new(procs, [{keypos, #proc.pid}])}}. + {ok, #state{tab = ets:new(procs, [ordered_set, {keypos, #proc.pid}])}}. handle_call(get_table, _From, State) -> {reply, State#state.tab, State}; @@ -27,33 +27,52 @@ handle_call(get_proc_count, _From, State) -> handle_call({get_proc, #doc{body={Props}}=DDoc, DDocKey}, From, State) -> Lang = couch_util:get_value(<<"language">>, Props, <<"javascript">>), - try get_procs(State#state.tab, Lang) of - [] -> - spawn_link(?MODULE, new_proc, [From, Lang, DDoc, DDocKey]), - {noreply, State}; - Procs -> - case proc_with_ddoc(DDoc, DDocKey, Procs) of - {ok, Proc0} -> - Client = element(1, From), - Proc = Proc0#proc{client = erlang:monitor(process, Client)}, - ets:insert(State#state.tab, Proc), - {reply, {ok, Proc, get_query_server_config()}, State}; - {error, Reason} -> - {reply, {error, Reason}, State} + IterFun = fun(Proc0, Acc) -> + case lists:member(DDocKey, Proc0#proc.ddoc_keys) of + true -> + {Client, _} = From, + Proc = Proc0#proc{client = erlang:monitor(process, Client)}, + ets:insert(State#state.tab, Proc), + {stop, Proc}; + false -> + {ok, Acc} end + end, + TeachFun = fun(Proc0, Acc) -> + try + {ok, Proc} = teach_ddoc(DDoc, DDocKey, Proc0), + {stop, Proc} + catch _:_ -> + {ok, Acc} + end + end, + try iter_procs(State#state.tab, Lang, IterFun, nil) of + {not_found, _} -> + case iter_procs(State#state.tab, Lang, TeachFun, nil) of + {not_found, _} -> + spawn_link(?MODULE, new_proc, [From, Lang, DDoc, DDocKey]), + {noreply, State}; + {ok, Proc} -> + {reply, {ok, Proc, get_query_server_config()}, State} + end; + {ok, Proc} -> + {reply, {ok, Proc, get_query_server_config()}, State} catch error:Reason -> ?LOG_ERROR("~p ~p ~p", [?MODULE, Reason, erlang:get_stacktrace()]), {reply, {error, Reason}, State} end; handle_call({get_proc, Lang}, {Client, _} = From, State) -> - try get_procs(State#state.tab, Lang) of - [] -> - spawn_link(?MODULE, new_proc, [From, Lang]), - {noreply, State}; - [Proc0|_] -> + IterFun = fun(Proc0, _Acc) -> Proc = Proc0#proc{client = erlang:monitor(process, Client)}, ets:insert(State#state.tab, Proc), + {stop, Proc} + end, + try iter_procs(State#state.tab, Lang, IterFun, nil) of + {not_found, _} -> + spawn_link(?MODULE, new_proc, [From, Lang]), + {noreply, State}; + {ok, Proc} -> {reply, {ok, Proc, get_query_server_config()}, State} catch error:Reason -> ?LOG_ERROR("~p ~p ~p", [?MODULE, Reason, erlang:get_stacktrace()]), @@ -128,10 +147,32 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. -get_procs(Tab, Lang) when is_binary(Lang) -> - get_procs(Tab, binary_to_list(Lang)); -get_procs(Tab, Lang) when is_list(Lang) -> - ets:match_object(Tab, #proc{lang=Lang, client=nil, _='_'}). + +iter_procs(Tab, Lang, Fun, Acc) when is_list(Lang) -> + iter_procs(Tab, list_to_binary(Lang), Fun, Acc); +iter_procs(Tab, Lang, Fun, Acc) -> + Pattern = #proc{lang=Lang, client=nil, _='_'}, + case ets:select_reverse(Tab, Pattern, 25) of + '$end_of_table' -> + {not_found, Acc}; + Continuation -> + iter_procs(Continuation, Fun, Acc) + end. + +iter_procs({[], Continuation0}, Fun, Acc) -> + case ets:select_reverse(Continuation0) of + '$end_of_table' -> + {not_found, Acc}; + Continuation1 -> + iter_procs(Continuation1, Fun, Acc) + end; +iter_procs({[Proc | Rest], Continuation}, Fun, Acc0) -> + case Fun(Proc, Acc0) of + {ok, Acc1} -> + iter_procs({Rest, Continuation}, Fun, Acc1); + {stop, Acc1} -> + {ok, Acc1} + end. new_proc(From, Lang) -> case new_proc_int(From, Lang) of |