aboutsummaryrefslogtreecommitdiffstats
path: root/lib/sasl/src/overload.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sasl/src/overload.erl')
-rw-r--r--lib/sasl/src/overload.erl224
1 files changed, 224 insertions, 0 deletions
diff --git a/lib/sasl/src/overload.erl b/lib/sasl/src/overload.erl
new file mode 100644
index 0000000000..3a9a51e8bf
--- /dev/null
+++ b/lib/sasl/src/overload.erl
@@ -0,0 +1,224 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(overload).
+
+-export([start_link/0, request/0, set_config_data/2,
+ get_overload_info/0]).
+
+-export([init/1, handle_call/3, handle_info/2, terminate/2,
+ format_status/2]).
+
+%%%-----------------------------------------------------------------
+%%% This is a rewrite of overload from BS.3, by Peter H�gfeldt.
+%%%
+%%% DESCRIPTION
+%%%
+%%% This module implements a server process that keeps record of the
+%%% intensity of calls of the request/0 function, and answers accept or
+%%% reject depending on if the current average intensity is not greater
+%%% than a specified maximum intensity.
+%%%
+%%% The intensity i is calculated according to the formula:
+%%% i(n) = exp(-K*(T(n) - T(n-1)))*i(n-1) + Kappa
+%%% where i(n) is the intensity at event n, Kappa is a constant, and
+%%% T(n) is the time at event n.
+%%%
+%%% The constant Kappa can be thought of as 1 / T, where T is the time
+%%% constant. Kappa is externally referred to as Weight.
+%%%
+%%% We keep track of two intensities: the total call intensity and the
+%%% intensity of accepted calls.
+%%%-----------------------------------------------------------------
+%%% TODO
+%%%
+%%% 3. Hysteresis.
+%%%
+%%%-----------------------------------------------------------------
+
+-record(state, {total = 0, accept = 0, max, prev_t = get_now(),
+ kappa, call_counts = {0, 0}, alarm = clear}).
+
+-define(clear_timeout, 30000).
+
+start_link() ->
+ gen_server:start_link({local, overload}, overload, [], []).
+
+init([]) ->
+ process_flag(priority, high),
+ MaxIntensity = fetch_config_data(overload_max_intensity),
+ Kappa = fetch_config_data(overload_weight),
+ {ok, #state{max = MaxIntensity, kappa = Kappa}}.
+
+%%-----------------------------------------------------------------
+%% Func: request/0
+%% Purpose: This is a request to proceed, e.g. a request to
+%% establish a call.
+%% Returns: accept | reject
+%%-----------------------------------------------------------------
+request() -> gen_server:call(overload, request).
+
+%%-----------------------------------------------------------------
+%% Func: set_config_data/2
+%% Purpose: Set configuration data, and reset intensities.
+%% Arguments: MaxIntensity (real > 0), Weight (real > 0).
+%% Returns: ok | {error, What}
+%% This function is for debugging purposes and is therefore not
+%% documented at all.
+%%-----------------------------------------------------------------
+set_config_data(MaxIntensity, Weight) ->
+ gen_server:call(overload, {set_config_data, MaxIntensity, Weight}).
+%%-----------------------------------------------------------------
+%% Func: get_overload_info/0
+%% Returns: A list of tagged items: TotalIntensity, AcceptIntensity,
+%% MaxIntensity, Weight, TotalRequests, AcceptedRequests.
+%%-----------------------------------------------------------------
+get_overload_info() -> gen_server:call(overload, get_overload_info).
+
+%%%-----------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%-----------------------------------------------------------------
+handle_call(request, _From, State) ->
+ #state{total = TI, accept = AI, kappa = Kappa, prev_t = PrevT,
+ alarm = Alarm} = State,
+ {TR, AR} = State#state.call_counts,
+ T = get_now(),
+ CurI = new_intensity(AI, T, PrevT, Kappa),
+ NewTI = new_intensity(TI, T, PrevT, Kappa) + Kappa,
+ if
+ CurI =< State#state.max ->
+ %% Hysteresis: If alarm is set, and current intensity has
+ %% fallen below 75% of max intensity, clear alarm.
+ NewAlarm = if
+ CurI =< 0.75*State#state.max ->
+ clear_alarm(Alarm);
+ true ->
+ Alarm
+ end,
+ {reply, accept, State#state{call_counts = {TR+1, AR+1},
+ prev_t = T, total = NewTI,
+ accept = CurI + Kappa,
+ alarm = NewAlarm},
+ ?clear_timeout};
+ true ->
+ %% Set alarm if not already set.
+ NewAlarm = set_alarm(Alarm),
+ {reply, reject,
+ State#state{call_counts = {TR+1, AR}, prev_t = T,
+ total = NewTI, accept = CurI,
+ alarm = NewAlarm},
+ ?clear_timeout}
+ end;
+handle_call({set_config_data, MaxIntensity, Weight}, _From, _State) ->
+ {reply, ok, #state{max = MaxIntensity, kappa = Weight},
+ ?clear_timeout};
+handle_call(get_overload_info, _From, State) ->
+ #state{max = MI, total = TI, accept = AI, kappa = Kappa,
+ prev_t = PrevT, call_counts = {TR, AR}} = State,
+ T = get_now(),
+ CurI = new_intensity(AI, T, PrevT, Kappa),
+ NewTI = new_intensity(TI, T, PrevT, Kappa),
+ Reply = [{total_intensity, NewTI}, {accept_intensity, CurI},
+ {max_intensity, MI}, {weight, Kappa},
+ {total_requests, TR}, {accepted_requests, AR}],
+ {reply, Reply, State#state{total = NewTI, accept = CurI},
+ ?clear_timeout}.
+
+handle_info(timeout, State) ->
+ #state{total = TI, accept = AI, kappa = Kappa, prev_t = PrevT,
+ alarm = Alarm} = State,
+ T = get_now(),
+ CurI = new_intensity(AI, T, PrevT, Kappa),
+ NewTI = new_intensity(TI, T, PrevT, Kappa),
+ if
+ CurI < 0.75* State#state.max ->
+ NewAlarm = clear_alarm(Alarm),
+ {noreply, State#state{total = NewTI, accept = CurI,
+ alarm = NewAlarm}};
+
+ true ->
+ {noreply, State#state{total = NewTI, accept = CurI},
+ ?clear_timeout}
+ end;
+
+handle_info(_, State) ->
+ {noreply, State, ?clear_timeout}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+%%-----------------------------------------------------------------
+%% Internal functions
+%%-----------------------------------------------------------------
+fetch_config_data(Tag) ->
+ case application:get_env(sasl, Tag) of
+ {ok, Value} -> Value;
+ _ -> fetch_default_data(Tag)
+ end.
+
+fetch_default_data(overload_max_intensity) -> 0.8;
+fetch_default_data(overload_weight) -> 0.1.
+
+set_alarm(clear) ->
+ alarm_handler:set_alarm({overload, []}),
+ set;
+set_alarm(Alarm) ->
+ Alarm.
+
+clear_alarm(set) ->
+ alarm_handler:clear_alarm(overload),
+ clear;
+clear_alarm(Alarm) ->
+ Alarm.
+
+%%-----------------------------------------------------------------
+%% The catch protects against floating-point exception.
+%%
+new_intensity(I, T, PrevT, K) ->
+ Diff = sub(T, PrevT)/1000,
+ case catch (I*math:exp(-K*Diff)) of
+ {'EXIT', _} -> % Assume zero.
+ 0.0;
+ Res ->
+ Res
+ end.
+
+%% Mask equal to 2^27 - 1, used below.
+-define(mask27, 16#7ffffff).
+
+%% Returns number of milliseconds in the range [0, 2^27 - 1]. Must have
+%% this since statistics(wall_clock) wraps. Having 2^27 -1 as the max
+%% assures that we always get non-negative integers. 2^27 milliseconds
+%% are approx. 37.28 hours.
+get_now() ->
+ element(1, statistics(wall_clock)) band ?mask27.
+
+%% Returns (X - Y) mod 2^27 (which is in the range [0, 2^27 - 1]).
+sub(X, Y) ->
+ (X + (bnot Y) + 1) band ?mask27.
+
+format_status(Opt, [PDict, #state{max = MI, total = TI, accept = AI,
+ kappa = K,
+ call_counts = {TR, AR}}]) ->
+ [{data, [{"Total Intensity", TI},
+ {"Accept Intensity", AI},
+ {"Max Intensity", MI},
+ {"Weight", K},
+ {"Total requests", TR},
+ {"Accepted requests", AR}]} |
+ misc_supp:format_pdict(Opt, PDict, [])].