summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon MacMullen <simon@rabbitmq.com>2012-11-12 13:16:08 +0000
committerSimon MacMullen <simon@rabbitmq.com>2012-11-12 13:16:08 +0000
commit9f3e80195c4cf485908e2a50823539657180dacd (patch)
tree2c1f8e88fdaf9d15d653b12a288110d00cc5e976
parent79a6cb25847fa11f5fce627a2c08390ab84b1463 (diff)
downloadrabbitmq-server-9f3e80195c4cf485908e2a50823539657180dacd.tar.gz
Background GC.
-rw-r--r--src/background_gc.erl81
-rw-r--r--src/rabbit.erl6
-rw-r--r--src/rabbit_misc.erl18
3 files changed, 105 insertions, 0 deletions
diff --git a/src/background_gc.erl b/src/background_gc.erl
new file mode 100644
index 00000000..36a3c387
--- /dev/null
+++ b/src/background_gc.erl
@@ -0,0 +1,81 @@
+%% 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]).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(HIBERNATE_AFTER_MIN, 1000).
+-define(DESIRED_HIBERNATE, 10000).
+
+-define(MAX_RATIO, 0.01).
+-define(IDEAL_INTERVAL, 5000).
+
+-record(state, {last_interval}).
+
+%%----------------------------------------------------------------------------
+
+start_link() ->
+ gen_server2:start_link(?MODULE, [], [{timeout, infinity}]).
+
+%%----------------------------------------------------------------------------
+
+init([]) ->
+ {ok, run_gc(#state{last_interval = ?IDEAL_INTERVAL}), hibernate,
+ {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}.
+
+handle_call(Msg, _From, State) ->
+ {stop, {unexpected_call, Msg}, State}.
+
+handle_cast(Msg, State) ->
+ {stop, {unexpected_cast, Msg}, State}.
+
+handle_info(run_gc, State) ->
+ {noreply, run_gc(State)};
+
+handle_info(Msg, State) ->
+ {stop, {unexpected_info, Msg}, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+terminate(_Reason, State) ->
+ State.
+
+%%----------------------------------------------------------------------------
+
+run_gc(State = #state{last_interval = LastInterval}) ->
+ {ok, Interval} = rabbit_misc:interval_operation(
+ fun do_gc/0, ?MAX_RATIO, ?IDEAL_INTERVAL, LastInterval),
+ erlang:send_after(Interval, self(), run_gc),
+ State#state{last_interval = Interval}.
+
+do_gc() ->
+ Sorted = lists:reverse(lists:sort(
+ [{memory(Pid), Pid} || Pid <- processes()])),
+ %% TODO select probabilisticly.
+ garbage_collect(element(2, hd(Sorted))),
+ ok.
+
+memory(Pid) -> case process_info(Pid, memory) of
+ {memory, M} -> M;
+ _ -> 0
+ end.
diff --git a/src/rabbit.erl b/src/rabbit.erl
index ef9f5f56..ba0fceaf 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]}},
+ {requires, networking}]}).
+
%%---------------------------------------------------------------------------
-include("rabbit_framing.hrl").
diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl
index 7e3cc3d7..956b6feb 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,17 @@ 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} -> IdealInterval
+ end}.