|
|
@@ -15,7 +15,7 @@
|
|
|
-module(emqx_misc).
|
|
|
|
|
|
-export([merge_opts/2, start_timer/2, start_timer/3, cancel_timer/1,
|
|
|
- proc_name/2, proc_stats/0, proc_stats/1]).
|
|
|
+ proc_name/2, proc_stats/0, proc_stats/1, conn_proc_mng_policy/1]).
|
|
|
|
|
|
%% @doc Merge options
|
|
|
-spec(merge_opts(list(), list()) -> list()).
|
|
|
@@ -36,14 +36,13 @@ start_timer(Interval, Dest, Msg) ->
|
|
|
erlang:start_timer(Interval, Dest, Msg).
|
|
|
|
|
|
-spec(cancel_timer(undefined | reference()) -> ok).
|
|
|
-cancel_timer(undefined) ->
|
|
|
- ok;
|
|
|
-cancel_timer(Timer) ->
|
|
|
- case catch erlang:cancel_timer(Timer) of
|
|
|
+cancel_timer(Timer) when is_reference(Timer) ->
|
|
|
+ case erlang:cancel_timer(Timer) of
|
|
|
false ->
|
|
|
receive {timeout, Timer, _} -> ok after 0 -> ok end;
|
|
|
_ -> ok
|
|
|
- end.
|
|
|
+ end;
|
|
|
+cancel_timer(_) -> ok.
|
|
|
|
|
|
-spec(proc_name(atom(), pos_integer()) -> atom()).
|
|
|
proc_name(Mod, Id) ->
|
|
|
@@ -59,3 +58,50 @@ proc_stats(Pid) ->
|
|
|
{value, {_, V}, Stats1} = lists:keytake(message_queue_len, 1, Stats),
|
|
|
[{mailbox_len, V} | Stats1].
|
|
|
|
|
|
+-define(DISABLED, 0).
|
|
|
+
|
|
|
+%% @doc Check self() process status against connection/session process management policy,
|
|
|
+%% return `continue | hibernate | {shutdown, Reason}' accordingly.
|
|
|
+%% `continue': There is nothing out of the ordinary.
|
|
|
+%% `hibernate': Nothing to process in my mailbox, and since this check is triggered
|
|
|
+%% by a timer, we assume it is a fat chance to continue idel, hence hibernate.
|
|
|
+%% `shutdown': Some numbers (message queue length or heap size have hit the limit),
|
|
|
+%% hence shutdown for greater good (system stability).
|
|
|
+-spec(conn_proc_mng_policy(#{message_queue_len := integer(),
|
|
|
+ total_heap_size := integer()
|
|
|
+ } | undefined) -> continue | hibernate | {shutdown, _}).
|
|
|
+conn_proc_mng_policy(#{message_queue_len := MaxMsgQueueLen,
|
|
|
+ total_heap_size := MaxTotalHeapSize
|
|
|
+ }) ->
|
|
|
+ Qlength = proc_info(message_queue_len),
|
|
|
+ Checks =
|
|
|
+ [{fun() -> is_message_queue_too_long(Qlength, MaxMsgQueueLen) end,
|
|
|
+ {shutdown, message_queue_too_long}},
|
|
|
+ {fun() -> is_heap_size_too_large(MaxTotalHeapSize) end,
|
|
|
+ {shutdown, total_heap_size_too_large}},
|
|
|
+ {fun() -> Qlength > 0 end, continue},
|
|
|
+ {fun() -> true end, hibernate}
|
|
|
+ ],
|
|
|
+ check(Checks);
|
|
|
+conn_proc_mng_policy(_) ->
|
|
|
+ %% disable by default
|
|
|
+ conn_proc_mng_policy(#{message_queue_len => 0, total_heap_size => 0}).
|
|
|
+
|
|
|
+check([{Pred, Result} | Rest]) ->
|
|
|
+ case Pred() of
|
|
|
+ true -> Result;
|
|
|
+ false -> check(Rest)
|
|
|
+ end.
|
|
|
+
|
|
|
+is_message_queue_too_long(Qlength, Max) ->
|
|
|
+ is_enabled(Max) andalso Qlength > Max.
|
|
|
+
|
|
|
+is_heap_size_too_large(Max) ->
|
|
|
+ is_enabled(Max) andalso proc_info(total_heap_size) > Max.
|
|
|
+
|
|
|
+is_enabled(Max) -> is_integer(Max) andalso Max > ?DISABLED.
|
|
|
+
|
|
|
+proc_info(Key) ->
|
|
|
+ {Key, Value} = erlang:process_info(self(), Key),
|
|
|
+ Value.
|
|
|
+
|