1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%%-----------------------------------------------------------------
%%% Overload protection configuration
%%! *** NOTE ***
%%! It's important that:
%%! SYNC_MODE_QLEN =< DROP_MODE_QLEN =< FLUSH_QLEN
%%! and that DROP_MODE_QLEN >= 2.
%%! Otherwise the process could end up in drop mode with no new
%%! log requests to process. This would cause all future requests
%%! to be dropped (no switch to async mode would ever take place).
%% This specifies the message_queue_len value where the log
%% requests switch from asynchronous casts to synchronous calls.
-define(SYNC_MODE_QLEN, 10).
%% Above this message_queue_len, log requests will be dropped,
%% i.e. no log requests get sent to the process.
-define(DROP_MODE_QLEN, 200).
%% Above this message_queue_len, the process will flush its mailbox
%% and only leave this number of messages in it.
-define(FLUSH_QLEN, 1000).
%% Never flush more than this number of messages in one go, or the
%% process will be unresponsive for seconds (keep this number as large
%% as possible or the mailbox could grow large).
-define(FLUSH_MAX_N, 5000).
%% BURST_LIMIT_MAX_COUNT is the max number of log requests allowed
%% to be written within a BURST_LIMIT_WINDOW_TIME time frame.
-define(BURST_LIMIT_ENABLE, true).
-define(BURST_LIMIT_MAX_COUNT, 500).
-define(BURST_LIMIT_WINDOW_TIME, 1000).
%% This enables/disables the feature to automatically terminate the
%% process if it gets too loaded (and can't keep up).
-define(OVERLOAD_KILL_ENABLE, false).
%% If the message_queue_len goes above this size even after
%% flushing has been performed, the process is terminated.
-define(OVERLOAD_KILL_QLEN, 20000).
%% If the memory usage exceeds this level, the process is terminated.
-define(OVERLOAD_KILL_MEM_SIZE, 3000000).
%% This is the default time to wait before restarting and accepting
%% new requests. The value 'infinity' disables restarts.
-define(OVERLOAD_KILL_RESTART_AFTER, 5000).
%% This is the time in milliseconds after last load message received
%% that we notify the callback about being idle.
-define(IDLE_DETECT_TIME, 100).
%%%-----------------------------------------------------------------
%%% Overload protection macros
-define(timestamp(), erlang:monotonic_time(microsecond)).
-define(get_mode(Tid),
case ets:lookup(Tid, mode) of
[{mode,M}] -> M;
_ -> async
end).
-define(set_mode(Tid, M),
begin ets:insert(Tid, {mode,M}), M end).
-define(change_mode(Tid, M0, M1),
if M0 == M1 ->
M0;
true ->
ets:insert(Tid, {mode,M1}),
M1
end).
-define(max(X1, X2),
if
X2 == undefined -> X1;
X2 > X1 -> X2;
true -> X1
end).
-define(diff_time(OS_T1, OS_T0), OS_T1-OS_T0).
%%%-----------------------------------------------------------------
%%% These macros enable statistics counters in the state of the
%%% process, which is useful for analysing the overload protection
%%% behaviour. These counters should not be included in code to be
%%% officially released (as some counters will grow very large over
%%% time).
%% -define(SAVE_STATS, true).
-ifdef(SAVE_STATS).
-define(merge_with_stats(STATE),
begin
TIME = ?timestamp(),
STATE#{start => TIME, time => {TIME,0},
flushes => 0, flushed => 0, drops => 0,
burst_drops => 0, casts => 0, calls => 0,
writes => 0, max_qlen => 0, max_time => 0,
freq => {TIME,0,0}} end).
-define(update_max_qlen(QLEN, STATE),
begin #{max_qlen := QLEN0} = STATE,
STATE#{max_qlen => ?max(QLEN0,QLEN)} end).
-define(update_calls_or_casts(CALL_OR_CAST, INC, STATE),
case CALL_OR_CAST of
cast ->
#{casts := CASTS0} = STATE,
STATE#{casts => CASTS0+INC};
call ->
#{calls := CALLS0} = STATE,
STATE#{calls => CALLS0+INC}
end).
-define(update_max_time(TIME, STATE),
begin #{max_time := TIME0} = STATE,
STATE#{max_time => ?max(TIME0,TIME)} end).
-define(update_other(OTHER, VAR, INCVAL, STATE),
begin #{OTHER := VAR} = STATE,
STATE#{OTHER => VAR+INCVAL} end).
-define(update_freq(TIME,STATE),
begin
case STATE of
#{freq := {START, 49, _}} ->
STATE#{freq => {TIME, 0, trunc(1000000*50/(?diff_time(TIME,START)))}};
#{freq := {START, N, FREQ}} ->
STATE#{freq => {START, N+1, FREQ}}
end end).
-define(update_time(TIME,STATE),
begin #{start := START} = STATE,
STATE#{time => {TIME,trunc((?diff_time(TIME,START))/1000000)}} end).
-else. % DEFAULT!
-define(merge_with_stats(STATE), STATE).
-define(update_max_qlen(_QLEN, STATE), STATE).
-define(update_calls_or_casts(_CALL_OR_CAST, _INC, STATE), STATE).
-define(update_max_time(_TIME, STATE), STATE).
-define(update_other(_OTHER, _VAR, _INCVAL, STATE), STATE).
-define(update_freq(_TIME, STATE), STATE).
-define(update_time(_TIME, STATE), STATE).
-endif.
%%%-----------------------------------------------------------------
%%% These macros enable callbacks that make it possible to analyse the
%%% overload protection behaviour from outside the process (including
%%% dropped requests on the client side). An external callback module
%%% (?OBSERVER_MOD) is required which is not part of the kernel
%%% application. For this reason, these callbacks should not be
%%% included in code to be officially released.
%%-define(OBSERVER_MOD, logger_test).
-ifdef(OBSERVER_MOD).
-define(start_observation(NAME), ?OBSERVER:start_observation(NAME)).
-define(observe(NAME,EVENT), ?OBSERVER:observe(NAME,EVENT)).
-else. % DEFAULT!
-define(start_observation(_NAME), ok).
-define(observe(_NAME,_EVENT), ok).
-endif.
|