109 lines
3.3 KiB
Erlang
109 lines
3.3 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. 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.
|
|
%%--------------------------------------------------------------------
|
|
-module(emqx_mgmt_cache).
|
|
|
|
-behaviour(gen_server).
|
|
|
|
-define(SYS_MEMORY_KEY, sys_memory).
|
|
-define(EXPIRED_MS, 3000).
|
|
%% -100ms to early update cache
|
|
-define(REFRESH_MS, ?EXPIRED_MS - 100).
|
|
-define(DEFAULT_BAD_MEMORY, {0, 0}).
|
|
|
|
-export([start_link/0, get_sys_memory/0]).
|
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
|
|
|
get_sys_memory() ->
|
|
case get_memory_from_cache() of
|
|
{ok, CacheMem} ->
|
|
erlang:send(?MODULE, refresh_sys_memory),
|
|
CacheMem;
|
|
stale ->
|
|
get_sys_memory_sync()
|
|
end.
|
|
|
|
get_sys_memory_sync() ->
|
|
try
|
|
gen_server:call(?MODULE, get_sys_memory, ?EXPIRED_MS)
|
|
catch
|
|
exit:{timeout, _} ->
|
|
?DEFAULT_BAD_MEMORY
|
|
end.
|
|
|
|
start_link() ->
|
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
|
|
|
init([]) ->
|
|
_ = ets:new(?MODULE, [set, named_table, public, {keypos, 1}]),
|
|
{ok, #{latest_refresh => 0}}.
|
|
|
|
handle_call(get_sys_memory, _From, State) ->
|
|
{Mem, NewState} = refresh_sys_memory(State),
|
|
{reply, Mem, NewState};
|
|
handle_call(_Request, _From, State) ->
|
|
{reply, ok, State}.
|
|
|
|
handle_cast(_Request, State) ->
|
|
{noreply, State}.
|
|
|
|
handle_info(refresh_sys_memory, State) ->
|
|
{_, NewState} = refresh_sys_memory(State),
|
|
{noreply, NewState};
|
|
handle_info(_Info, State) ->
|
|
{noreply, State}.
|
|
|
|
terminate(_Reason, _State) ->
|
|
ok.
|
|
|
|
code_change(_OldVsn, State, _Extra) ->
|
|
{ok, State}.
|
|
|
|
%%%===================================================================
|
|
%%% Internal functions
|
|
%%%===================================================================
|
|
|
|
refresh_sys_memory(State = #{latest_refresh := LatestRefresh}) ->
|
|
Now = now_millisecond(),
|
|
case Now - LatestRefresh >= ?REFRESH_MS of
|
|
true ->
|
|
do_refresh_sys_memory(Now, State);
|
|
false ->
|
|
case get_memory_from_cache() of
|
|
stale -> do_refresh_sys_memory(Now, State);
|
|
{ok, Mem} -> {Mem, State}
|
|
end
|
|
end.
|
|
|
|
do_refresh_sys_memory(RefreshAt, State) ->
|
|
NewMem = load_ctl:get_sys_memory(),
|
|
NewExpiredAt = now_millisecond() + ?EXPIRED_MS,
|
|
ets:insert(?MODULE, {?SYS_MEMORY_KEY, {NewMem, NewExpiredAt}}),
|
|
{NewMem, State#{latest_refresh => RefreshAt}}.
|
|
|
|
get_memory_from_cache() ->
|
|
case ets:lookup(?MODULE, ?SYS_MEMORY_KEY) of
|
|
[] ->
|
|
stale;
|
|
[{_, {Mem, ExpiredAt}}] ->
|
|
case now_millisecond() < ExpiredAt of
|
|
true -> {ok, Mem};
|
|
false -> stale
|
|
end
|
|
end.
|
|
|
|
now_millisecond() ->
|
|
erlang:system_time(millisecond).
|