From aa217d8f0e029d2d9f3e16499c7951d09692e79d Mon Sep 17 00:00:00 2001 From: Thomas O'Dowd Date: Wed, 7 Apr 2010 19:14:44 +0900 Subject: [PATCH] Added a new configuration parameter to allow the administrator to control the erlang virtual machines garbage collection knobs when spawning a new process to handle incoming connections. The option is called "process_options". Its default value is the empty list which uses erlangs default options as is the case now. This is useful for long lived connections which can generate a lot of garbage if left untapped. Use with care :-) --- include/yaws.hrl | 6 +++++- man/yaws.conf.5 | 9 ++++++++ scripts/yaws.conf.template | 7 +++++++ src/yaws_config.erl | 42 ++++++++++++++++++++++++++++++++++++++ src/yaws_server.erl | 9 +++++++- 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/include/yaws.hrl b/include/yaws.hrl index 575df7481..5656d840c 100644 --- a/include/yaws.hrl +++ b/include/yaws.hrl @@ -77,7 +77,11 @@ max_num_cached_files = 400, max_num_cached_bytes = 1000000, %% 1 MEG max_size_cached_file = 8000, - max_connections = nolimit, %% max number of TCP connections + max_connections = nolimit, %% max number of TCP connections + process_options = [], %% Override default connection handler processes + %% spawn options for performance/memory tuning. + %% [] | [{fullsweep_after, Number}, {min_heap_size, Size}] + %% other options such as monitor, link are ignored. large_file_chunk_size = 10240, mnesia_dir = [], log_wrap_size = 10000000, % wrap logs after 10M diff --git a/man/yaws.conf.5 b/man/yaws.conf.5 index 30f24e5cf..4c5d32aa1 100644 --- a/man/yaws.conf.5 +++ b/man/yaws.conf.5 @@ -107,6 +107,15 @@ Set this value to control the maximum number of connections from HTTP clients into the server. This is implemented by closing the last socket if the limit threshold is reached. +.TP +\fB process_options = "[]" | "[{fullsweep_after, int()} | {min_heap_size, int()}]"\fR +Override the garbage collection option parameters for processes +which handle new connections. Useful for systems which expect long lived +connections which handle a lot of data. The default value is erlangs +default which does minimal garbage collection until the process dies. +The value type is a quoted string which contains an erlang proplist. +See erlangs erlang:spawn_opt/4 function for more details. + .TP \fB log_wrap_size = Integer\fR The logs written by yaws are all wrap logs, the default value at the diff --git a/scripts/yaws.conf.template b/scripts/yaws.conf.template index b97cc96ce..3e6901b56 100644 --- a/scripts/yaws.conf.template +++ b/scripts/yaws.conf.template @@ -29,6 +29,13 @@ include_dir = %yawsdir%/examples/include # max number of connections from clients into the server max_connections = nolimit +# Override the garbage collection option parameters for processes +# which handle new connections. Useful for systems which expect long lived +# connections which handle a lot of data. The default value is erlangs +# default. Valid options are {fullsweep_after, X} and/or {min_heap_size, Y} where +# X and Y are integers. See erlangs erlang:spawn_opt/4 function for more details. +# The value type is a quoted string containing an erlang proplist +process_options = "[]" # This is a debug variable, possible values are http | traffic | false # It is also possible to set the trace (possibly to the tty) while diff --git a/src/yaws_config.erl b/src/yaws_config.erl index dc943d5c0..50f93c52a 100644 --- a/src/yaws_config.erl +++ b/src/yaws_config.erl @@ -584,6 +584,15 @@ fload(FD, globals, GC, C, Cs, Lno, Chars) -> {error, ?F("Expect integer at line ~w", [Lno])} end; + ["process_options", '=', POpts] -> + case parse_process_options(POpts) of + {ok, ProcList} -> + fload(FD, globals, GC#gconf{process_options=ProcList}, + C, Cs, Lno+1, Next); + {error, Str} -> + {error, ?F("~s at line ~w", [Str, Lno])} + end; + ["log_wrap_size", '=', Int] -> case (catch list_to_integer(Int)) of I when is_integer(I) -> @@ -1467,6 +1476,39 @@ is_string_char([C|T]) -> is_special(C) -> lists:member(C, [$=, $<, $>, $,]). +%% parse the argument string PLString which can either be the undefined atom +%% or a proplist. Currently the only supported keys are fullsweep_after and +%% min_heap_size. Any other key/values are ignored. +parse_process_options(PLString) -> + case erl_scan:string(PLString ++ ".") of + {ok, PLTokens, _} -> + case erl_parse:parse_term(PLTokens) of + {ok, undefined} -> + {ok, []}; + {ok, []} -> + {ok, []}; + {ok, [Hd|_Tl]=PList} when is_atom(Hd); is_tuple(Hd) -> + %% create new safe proplist of desired options + {ok, proplists_int_copy([], PList, [fullsweep_after, min_heap_size])}; + _ -> + {error, "Expect undefined or proplist"} + end; + _ -> + {error, "Expect undefined or proplist"} + end. + +%% copy proplist integer values for the given keys from the +%% Src proplist to the Dest proplist. Ignored keys that are not +%% found or have non-integer values. Returns the new Dest proplist. +proplists_int_copy(Dest, _Src, []) -> + Dest; +proplists_int_copy(Dest, Src, [Key|NextKeys]) -> + case proplists:get_value(Key, Src) of + Val when is_integer(Val) -> + proplists_int_copy([{Key, Val}|Dest], Src, NextKeys); + _ -> + proplists_int_copy(Dest, Src, NextKeys) + end. parse_soap_srv_mods(['<', Module, ',' , Handler, ',', WsdlFile, '>' | Tail], Ack) -> diff --git a/src/yaws_server.erl b/src/yaws_server.erl index 37d32cbc4..811614123 100644 --- a/src/yaws_server.erl +++ b/src/yaws_server.erl @@ -908,7 +908,14 @@ initial_acceptor(GS) -> acceptor(GS) -> - proc_lib:spawn_link(?MODULE, acceptor0, [GS, self()]). + case (GS#gs.gconf)#gconf.process_options of + [] -> + proc_lib:spawn_link(?MODULE, acceptor0, [GS, self()]); + Opts -> + %% as we tightly controlled what is set in options, we can blindly add "link" to + %% get a linked process as per default case and use the provided options. + proc_lib:spawn_opt(?MODULE, acceptor0, [GS, self()], [link | Opts]) + end. acceptor0(GS, Top) -> ?TC([{record, GS, gs}]), put(gc, GS#gs.gconf),