summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/background_gc.erl78
-rw-r--r--src/rabbit.erl6
-rw-r--r--src/rabbit_alarm.erl5
-rw-r--r--src/rabbit_misc.erl19
4 files changed, 105 insertions, 3 deletions
diff --git a/src/background_gc.erl b/src/background_gc.erl
new file mode 100644
index 00000000..7c68a177
--- /dev/null
+++ b/src/background_gc.erl
@@ -0,0 +1,78 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (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.mozilla.org/MPL/
+%%
+%% 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.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is VMware, Inc.
+%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved.
+%%
+
+-module(background_gc).
+
+-behaviour(gen_server2).
+
+-export([start_link/0, run/0]).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(MAX_RATIO, 0.01).
+-define(IDEAL_INTERVAL, 60000).
+
+-record(state, {last_interval}).
+
+%%----------------------------------------------------------------------------
+
+-ifdef(use_specs).
+
+-spec(start_link/0 :: () -> {'ok', pid()} | {'error', any()}).
+-spec(run/0 :: () -> 'ok').
+
+-endif.
+
+%%----------------------------------------------------------------------------
+
+start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [],
+ [{timeout, infinity}]).
+
+run() -> gen_server2:cast(?MODULE, run).
+
+%%----------------------------------------------------------------------------
+
+init([]) -> {ok, interval_gc(#state{last_interval = ?IDEAL_INTERVAL})}.
+
+handle_call(Msg, _From, State) ->
+ {stop, {unexpected_call, Msg}, {unexpected_call, Msg}, State}.
+
+handle_cast(run, State) -> gc(), {noreply, State};
+
+handle_cast(Msg, State) -> {stop, {unexpected_cast, Msg}, State}.
+
+handle_info(run, State) -> {noreply, interval_gc(State)};
+
+handle_info(Msg, State) -> {stop, {unexpected_info, Msg}, State}.
+
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
+
+terminate(_Reason, State) -> State.
+
+%%----------------------------------------------------------------------------
+
+interval_gc(State = #state{last_interval = LastInterval}) ->
+ {ok, Interval} = rabbit_misc:interval_operation(
+ fun gc/0, ?MAX_RATIO, ?IDEAL_INTERVAL, LastInterval),
+ erlang:send_after(Interval, self(), run),
+ State#state{last_interval = Interval}.
+
+gc() ->
+ [garbage_collect(P) || P <- processes(),
+ {status, waiting} == process_info(P, status)],
+ garbage_collect(), %% since we will never be waiting...
+ ok.
diff --git a/src/rabbit.erl b/src/rabbit.erl
index ef9f5f56..c3a6d283 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -179,6 +179,12 @@
{mfa, {rabbit_node_monitor, notify_node_up, []}},
{requires, networking}]}).
+-rabbit_boot_step({background_gc,
+ [{description, "background garbage collection"},
+ {mfa, {rabbit_sup, start_restartable_child,
+ [background_gc]}},
+ {enables, networking}]}).
+
%%---------------------------------------------------------------------------
-include("rabbit_framing.hrl").
diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl
index 675d3697..d7d4d82a 100644
--- a/src/rabbit_alarm.erl
+++ b/src/rabbit_alarm.erl
@@ -57,9 +57,8 @@ start() ->
rabbit_sup:start_restartable_child(
vm_memory_monitor, [MemoryWatermark,
fun (Alarm) ->
- R = set_alarm(Alarm),
- [garbage_collect(P) || P <- processes()],
- R
+ background_gc:run(),
+ set_alarm(Alarm)
end,
fun clear_alarm/1]),
{ok, DiskLimit} = application:get_env(disk_free_limit),
diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl
index 7e3cc3d7..137ccf20 100644
--- a/src/rabbit_misc.erl
+++ b/src/rabbit_misc.erl
@@ -65,6 +65,7 @@
-export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]).
-export([check_expiry/1]).
-export([base64url/1]).
+-export([interval_operation/4]).
%% Horrible macro to use in guards
-define(IS_BENIGN_EXIT(R),
@@ -235,6 +236,9 @@
-spec(term_to_json/1 :: (any()) -> any()).
-spec(check_expiry/1 :: (integer()) -> rabbit_types:ok_or_error(any())).
-spec(base64url/1 :: (binary()) -> string()).
+-spec(interval_operation/4 ::
+ (thunk(A), float(), non_neg_integer(), non_neg_integer())
+ -> {A, non_neg_integer()}).
-endif.
@@ -1015,3 +1019,18 @@ base64url(In) ->
($\=, Acc) -> Acc;
(Chr, Acc) -> [Chr | Acc]
end, [], base64:encode_to_string(In))).
+
+%% Ideally, you'd want Fun to run every IdealInterval. but you don't
+%% want it to take more than MaxRatio of IdealInterval. So if it takes
+%% more then you want to run it less often. So we time how long it
+%% takes to run, and then suggest how long you should wait before
+%% running it again. Times are in millis.
+interval_operation(Fun, MaxRatio, IdealInterval, LastInterval) ->
+ {Micros, Res} = timer:tc(Fun),
+ {Res, case {Micros > 1000 * (MaxRatio * IdealInterval),
+ Micros > 1000 * (MaxRatio * LastInterval)} of
+ {true, true} -> round(LastInterval * 1.5);
+ {true, false} -> LastInterval;
+ {false, false} -> lists:max([IdealInterval,
+ round(LastInterval / 1.5)])
+ end}.