diff options
author | Simon MacMullen <simon@lshift.net> | 2008-11-25 13:07:59 +0000 |
---|---|---|
committer | Simon MacMullen <simon@lshift.net> | 2008-11-25 13:07:59 +0000 |
commit | cc86260fcf9dbc5432df6365113f73197120f4d4 (patch) | |
tree | e39a013a55034ef27379976e67df7ebc3b7ac9c9 | |
parent | d0bc61aef26fb7f1d9d97e1ca2e0e3d71fd6a4d7 (diff) | |
download | rabbitmq-server-cc86260fcf9dbc5432df6365113f73197120f4d4.tar.gz |
First pass at a simple memory supervisor for Linux.
-rw-r--r-- | src/rabbit.erl | 2 | ||||
-rw-r--r-- | src/rabbit_alarm.erl | 44 | ||||
-rw-r--r-- | src/rabbit_linux_memory.erl | 108 |
3 files changed, 138 insertions, 16 deletions
diff --git a/src/rabbit.erl b/src/rabbit.erl index a33c5b7b..cd2bda97 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -29,7 +29,7 @@ -export([start/0, stop/0, stop_and_halt/0, status/0, rotate_logs/1]). --export([start/2, stop/1]). +-export([start/2, stop/1, start_child/1]). -export([log_location/1]). diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index d9c1c450..5ba88a30 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -50,21 +50,35 @@ %%---------------------------------------------------------------------------- start() -> - %% The default memsup check interval is 1 minute, which is way too - %% long - rabbit can gobble up all memory in a matter of - %% seconds. Unfortunately the memory_check_interval configuration - %% parameter and memsup:set_check_interval/1 function only provide - %% a granularity of minutes. So we have to peel off one layer of - %% the API to get to the underlying layer which operates at the - %% granularity of milliseconds. - %% - %% Note that the new setting will only take effect after the first - %% check has completed, i.e. after one minute. So if rabbit eats - %% all the memory within the first minute after startup then we - %% are out of luck. - ok = os_mon:call(memsup, {set_check_interval, ?MEMSUP_CHECK_INTERVAL}, - infinity), - + case os:type() of + {unix, linux} -> + %% memsup doesn't take account of buffers or cache when considering + %% "free" memory - therefore on Linux we can get memory alarms + %% very easily without any pressure existing on memory at all. + %% Therefore we need to use our own simple memory monitor + + supervisor:terminate_child(os_mon_sup, memsup), + supervisor:delete_child(os_mon_sup, memsup), + rabbit:start_child(rabbit_linux_memory), + + ok; + _ -> + %% The default memsup check interval is 1 minute, which is way too + %% long - rabbit can gobble up all memory in a matter of + %% seconds. Unfortunately the memory_check_interval configuration + %% parameter and memsup:set_check_interval/1 function only provide + %% a granularity of minutes. So we have to peel off one layer of + %% the API to get to the underlying layer which operates at the + %% granularity of milliseconds. + %% + %% Note that the new setting will only take effect after the first + %% check has completed, i.e. after one minute. So if rabbit eats + %% all the memory within the first minute after startup then we + %% are out of luck. + + ok = os_mon:call(memsup, {set_check_interval, ?MEMSUP_CHECK_INTERVAL}, + infinity) + end, ok = alarm_handler:add_alarm_handler(?MODULE). stop() -> diff --git a/src/rabbit_linux_memory.erl b/src/rabbit_linux_memory.erl new file mode 100644 index 00000000..69708519 --- /dev/null +++ b/src/rabbit_linux_memory.erl @@ -0,0 +1,108 @@ +%% 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 Developers of the Original Code are LShift Ltd., +%% Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd. +%% +%% Portions created by LShift Ltd., Cohesive Financial Technologies +%% LLC., and Rabbit Technologies Ltd. are Copyright (C) 2007-2008 +%% LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit +%% Technologies Ltd.; +%% +%% All Rights Reserved. +%% +%% Contributor(s): ______________________________________. +%% + +-module(rabbit_linux_memory). + +-behaviour(gen_server). + +-export([start_link/0]). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-define(SERVER, ?MODULE). + +-define(MEMORY_CHECK_INTERVAL, 1000). +-define(MEMORY_CHECK_FRACTION, 0.95). + +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + + +init(_Args) -> + {ok, no_alarm, ?MEMORY_CHECK_INTERVAL}. + + +handle_call(_Request, _From, State) -> + {noreply, State, ?MEMORY_CHECK_INTERVAL}. + + +handle_cast(_Request, State) -> + {noreply, State, ?MEMORY_CHECK_INTERVAL}. + + +handle_info(_Info, State) -> + File = read_proc_file("/proc/meminfo"), + Lines = string:tokens(File, "\n"), + Dict = dict:from_list(split_and_parse_lines(Lines, [])), + MemTotal = dict:fetch("MemTotal", Dict), + MemUsed = MemTotal + - dict:fetch("MemFree", Dict) + - dict:fetch("Buffers", Dict) + - dict:fetch("Cached", Dict), + if + MemUsed / MemTotal > ?MEMORY_CHECK_FRACTION -> + NewState = alarm; + true -> + NewState = no_alarm + end, + case {State, NewState} of + {no_alarm, alarm} -> + alarm_handler:set_alarm({system_memory_high_watermark, []}), + ok; + {alarm, no_alarm} -> + alarm_handler:clear_alarm(system_memory_high_watermark), + ok; + _ -> + ok + end, + {noreply, NewState, ?MEMORY_CHECK_INTERVAL}. + +%% file:read_file does not work on files in /proc as it seems to get the size +%% of the file first and then read that many bytes. But files in /proc always +%% have length 0, we just have to read until we get eof. +read_proc_file(File) -> + {ok, IoDevice} = file:open(File, [read, raw]), + {ok, Res} = file:read(IoDevice, 1000000), + Res. + +%% A line looks like "FooBar: 123456 kB" +split_and_parse_lines([], Acc) -> Acc; +split_and_parse_lines([Line | Rest], Acc) -> + Name = line_element(Line, 1), + ValueString = line_element(Line, 2), + Value = list_to_integer(string:sub_word(ValueString, 1)), + split_and_parse_lines(Rest, [{Name, Value} | Acc]). + +line_element(Line, Count) -> + string:strip(string:sub_word(Line, Count, $:)). + + +terminate(_Reason, _State) -> + ok. + + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. |