summaryrefslogtreecommitdiff
path: root/apps/couch
diff options
context:
space:
mode:
authorPaul J. Davis <paul.joseph.davis@gmail.com>2011-12-19 13:39:49 -0600
committerRobert Newson <robert.newson@cloudant.com>2012-11-14 17:27:34 +0000
commitc15440cd74b04ba88f9af0a59f9505478d314d38 (patch)
treed7c87e25efc5f85fc1a7b4b6375cff8a79e43d62 /apps/couch
parent017ba1c626e565d3b70c90ca92496d2602e4a909 (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.erl87
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