diff options
author | Simon MacMullen <simon@rabbitmq.com> | 2012-11-12 13:16:08 +0000 |
---|---|---|
committer | Simon MacMullen <simon@rabbitmq.com> | 2012-11-12 13:16:08 +0000 |
commit | 9f3e80195c4cf485908e2a50823539657180dacd (patch) | |
tree | 2c1f8e88fdaf9d15d653b12a288110d00cc5e976 | |
parent | 79a6cb25847fa11f5fce627a2c08390ab84b1463 (diff) | |
download | rabbitmq-server-9f3e80195c4cf485908e2a50823539657180dacd.tar.gz |
Background GC.
-rw-r--r-- | src/background_gc.erl | 81 | ||||
-rw-r--r-- | src/rabbit.erl | 6 | ||||
-rw-r--r-- | src/rabbit_misc.erl | 18 |
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}. |