[erlang-questions] [patch] Suggested improvement to supervisor.erl
Jay Nelson
jay@REDACTED
Fri May 16 15:17:03 CEST 2008
I have heard others in the past complain about a simple_one_for_one
supervisor which launches thousands of children. When a node becomes
sluggish (typically running out of memory or too many processes), it
may be useful to look at the supervisors to find the culprit of the
excessive demand. The problem is that which_children/1 will generate
a list of all the children, thus exacerbating the low resource issue.
I recently came across a situation where I wanted to have a count of
the number of children, but didn't want to use additional memory in
determining the number of processes. My solution was a function
count_children/1 which returns a property list of counts by
traversing the existing supervisor children list without copying it.
The resulting property list has the form:
[{specs, integer()}, {active, integer()}, {supervisors, integer()},
{workers, integer()}]
In code, the results should of course be treated as a real property
list, rather than relying on the order of attributes specified
above. Specs counts the number of child specifications present,
active is the number of live child processes, supervisors is the
number of supervisor processes and workers is the number of child
processes including those that are no longer alive but have not been
removed from the supervisor's list.
jay
--------------
Below is a patch that adds count_children/1:
--- stdlib/src/supervisor.erl.orig 2008-04-20 23:09:48.000000000
-0700
+++ stdlib/src/supervisor.erl 2008-04-20 23:19:27.000000000 -0700
@@ -23,7 +23,7 @@
-export([start_link/2,start_link/3,
start_child/2, restart_child/2,
delete_child/2, terminate_child/2,
- which_children/1,
+ which_children/1, count_children/1,
check_childspecs/1]).
-export([behaviour_info/1]).
@@ -94,6 +94,9 @@
which_children(Supervisor) ->
call(Supervisor, which_children).
+count_children(Supervisor) ->
+ call(Supervisor, count_children).
+
call(Supervisor, Req) ->
gen_server:call(Supervisor, Req, infinity).
@@ -296,7 +299,49 @@
{Name, Pid, ChildType, Mods}
end,
State#state.children),
- {reply, Resp, State}.
+ {reply, Resp, State};
+
+handle_call(count_children, _From, State) when ?is_simple(State) ->
+ [#child{child_type = CT}] = State#state.children,
+ {Active, Count} =
+ ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) ->
+ if is_pid(Pid) -> {Alive+1, Tot +1};
+ true -> {Alive, Tot + 1} end
+ end, {0, 0}, State#state.dynamics),
+ Reply = case CT of
+ supervisor -> [{specs, 1}, {active, Active},
+ {supervisors, Count}, {workers, 0}];
+ worker -> [{specs, 1}, {active, Active},
+ {supervisors, 0}, {workers, Count}]
+ end,
+ {reply, Reply, State};
+
+handle_call(count_children, _From, State) ->
+
+ %% Specs and children are together on the children list...
+ [Specs, Active, Supers, Workers] =
+ lists:foldl(fun(Child, Counts) ->
+ count_child(Child, Counts)
+ end, [0,0,0,0], State#state.children),
+
+ %% Reformat counts to a property list.
+ Reply = [{specs, Specs}, {active, Active},
+ {supervisors, Supers}, {workers, Workers}],
+ {reply, Reply, State}.
+
+
+count_child(#child{pid = Pid, child_type = worker},
+ [Specs, Active, Supers, Workers]) ->
+ case is_pid(Pid) andalso is_process_alive(Pid) of
+ true -> [Specs+1, Active+1, Supers, Workers+1];
+ false -> [Specs+1, Active, Supers, Workers+1]
+ end;
+count_child(#child{pid = Pid, child_type = supervisor},
+ [Specs, Active, Supers, Workers]) ->
+ case is_pid(Pid) andalso is_process_alive(Pid) of
+ true -> [Specs+1, Active+1, Supers+1, Workers];
+ false -> [Specs+1, Active, Supers+1, Workers]
+ end.
More information about the erlang-questions
mailing list