summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon MacMullen <simon@lshift.net>2008-11-25 13:07:59 +0000
committerSimon MacMullen <simon@lshift.net>2008-11-25 13:07:59 +0000
commitcc86260fcf9dbc5432df6365113f73197120f4d4 (patch)
treee39a013a55034ef27379976e67df7ebc3b7ac9c9
parentd0bc61aef26fb7f1d9d97e1ca2e0e3d71fd6a4d7 (diff)
downloadrabbitmq-server-cc86260fcf9dbc5432df6365113f73197120f4d4.tar.gz
First pass at a simple memory supervisor for Linux.
-rw-r--r--src/rabbit.erl2
-rw-r--r--src/rabbit_alarm.erl44
-rw-r--r--src/rabbit_linux_memory.erl108
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}.