summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Majkowski <majek@lshift.net>2009-09-25 17:31:13 +0100
committerMarek Majkowski <majek@lshift.net>2009-09-25 17:31:13 +0100
commita7a34f7bc76bb4b4cb1e37adfeb4a41e31381294 (patch)
treecd2dde5adc439d93da592884fee7db8790c45be9
parent08c140a80ca86338fbfcdc88219a1dd1507070a5 (diff)
downloadrabbitmq-server-a7a34f7bc76bb4b4cb1e37adfeb4a41e31381294.tar.gz
qa fixes, some refactoring
-rw-r--r--ebin/rabbit_app.in3
-rwxr-xr-xscripts/rabbitmq-server8
-rwxr-xr-xscripts/rabbitmq-server.bat8
-rwxr-xr-xscripts/rabbitmq-service.bat8
-rw-r--r--src/rabbit.erl10
-rw-r--r--src/rabbit_alarm.erl12
-rw-r--r--src/rabbit_memguard.erl187
7 files changed, 156 insertions, 80 deletions
diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in
index 8796b425..9dea663a 100644
--- a/ebin/rabbit_app.in
+++ b/ebin/rabbit_app.in
@@ -21,5 +21,4 @@
{default_user, <<"guest">>},
{default_pass, <<"guest">>},
{default_vhost, <<"/">>},
- {default_permissions, [<<".*">>, <<".*">>, <<".*">>]},
- {memory_high_watermark, 0.85}]}]}.
+ {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}]}]}.
diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server
index ffd97b35..dcc23f79 100755
--- a/scripts/rabbitmq-server
+++ b/scripts/rabbitmq-server
@@ -100,12 +100,8 @@ exec erl \
-sasl errlog_type error \
-kernel error_logger '{file,"'${RABBITMQ_LOGS}'"}' \
-sasl sasl_error_logger '{file,"'${RABBITMQ_SASL_LOGS}'"}' \
- -os_mon start_cpu_sup true \
- -os_mon start_disksup false \
- -os_mon start_memsup true \
- -os_mon start_os_sup false \
- -os_mon memsup_system_only false \
- -os_mon system_memory_high_watermark 0.95 \
+ -os_mon start_memsup false \
+ -os_mon system_memory_high_watermark 0.70 \
-mnesia dir "\"${RABBITMQ_MNESIA_DIR}\"" \
${RABBITMQ_CLUSTER_CONFIG_OPTION} \
${RABBITMQ_SERVER_START_ARGS} \
diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat
index b83d44ca..3f5ac3e7 100755
--- a/scripts/rabbitmq-server.bat
+++ b/scripts/rabbitmq-server.bat
@@ -125,12 +125,8 @@ if exist "%RABBITMQ_EBIN_ROOT%\rabbit.boot" (
%RABBITMQ_SERVER_ERL_ARGS% ^
-sasl errlog_type error ^
-sasl sasl_error_logger {file,\""%RABBITMQ_LOG_BASE%/%RABBITMQ_NODENAME%-sasl.log"\"} ^
--os_mon start_cpu_sup true ^
--os_mon start_disksup false ^
--os_mon start_memsup true ^
--os_mon start_os_sup false ^
--os_mon memsup_system_only false ^
--os_mon system_memory_high_watermark 1.0 ^
+-os_mon start_memsup false ^
+-os_mon system_memory_high_watermark 0.7 ^
-mnesia dir \""%RABBITMQ_MNESIA_DIR%"\" ^
%CLUSTER_CONFIG% ^
%RABBITMQ_SERVER_START_ARGS% ^
diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat
index dcb929b1..480acdda 100755
--- a/scripts/rabbitmq-service.bat
+++ b/scripts/rabbitmq-service.bat
@@ -170,12 +170,8 @@ set ERLANG_SERVICE_ARGUMENTS= ^
-kernel error_logger {file,\""%RABBITMQ_LOG_BASE%/%RABBITMQ_NODENAME%.log"\"} ^
-sasl errlog_type error ^
-sasl sasl_error_logger {file,\""%RABBITMQ_LOG_BASE%/%RABBITMQ_NODENAME%-sasl.log"\"} ^
--os_mon start_cpu_sup true ^
--os_mon start_disksup false ^
--os_mon start_memsup true ^
--os_mon start_os_sup false ^
--os_mon memsup_system_only false ^
--os_mon system_memory_high_watermark 1.0 ^
+-os_mon start_memsup false ^
+-os_mon system_memory_high_watermark 0.7 ^
-mnesia dir \""%RABBITMQ_MNESIA_DIR%"\" ^
%CLUSTER_CONFIG% ^
%RABBITMQ_SERVER_START_ARGS% ^
diff --git a/src/rabbit.erl b/src/rabbit.erl
index 875021aa..7df187cf 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -137,7 +137,9 @@ start(normal, []) ->
check_empty_content_body_frame_size(),
ok = rabbit_alarm:start(),
- case application:get_env(memory_high_watermark) of
+ MemoryWatermark =
+ application:get_env(os_mon, system_memory_high_watermark),
+ case MemoryWatermark of
{ok, false} -> ok;
{ok, off} -> ok;
{ok, Float} -> start_child(rabbit_memguard, [Float])
@@ -251,10 +253,7 @@ print_banner() ->
io:nl().
start_child(Mod) ->
- {ok,_} = supervisor:start_child(rabbit_sup,
- {Mod, {Mod, start_link, []},
- transient, 100, worker, [Mod]}),
- ok.
+ start_child(Mod, []).
start_child(Mod, Args) ->
{ok,_} = supervisor:start_child(rabbit_sup,
@@ -262,7 +261,6 @@ start_child(Mod, Args) ->
transient, 100, worker, [Mod]}),
ok.
-
ensure_working_log_handlers() ->
Handlers = gen_event:which_handlers(error_logger),
ok = ensure_working_log_handler(error_logger_file_h,
diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl
index c1997554..5abd10bd 100644
--- a/src/rabbit_alarm.erl
+++ b/src/rabbit_alarm.erl
@@ -38,7 +38,7 @@
-export([init/1, handle_call/2, handle_event/2, handle_info/2,
terminate/2, code_change/3]).
--record(alarms, {alertees, memory_high_watermark = false}).
+-record(alarms, {alertees, vm_memory_high_watermark = false}).
%%----------------------------------------------------------------------------
@@ -72,7 +72,7 @@ init([]) ->
handle_call({register, Pid, HighMemMFA},
State = #alarms{alertees = Alertess}) ->
_MRef = erlang:monitor(process, Pid),
- case State#alarms.memory_high_watermark of
+ case State#alarms.vm_memory_high_watermark of
true -> {M, F, A} = HighMemMFA,
ok = erlang:apply(M, F, A ++ [Pid, true]);
false -> ok
@@ -83,13 +83,13 @@ handle_call({register, Pid, HighMemMFA},
handle_call(_Request, State) ->
{ok, not_understood, State}.
-handle_event({set_alarm, {memory_high_watermark, []}}, State) ->
+handle_event({set_alarm, {vm_memory_high_watermark, []}}, State) ->
ok = alert(true, State#alarms.alertees),
- {ok, State#alarms{memory_high_watermark = true}};
+ {ok, State#alarms{vm_memory_high_watermark = true}};
-handle_event({clear_alarm, memory_high_watermark}, State) ->
+handle_event({clear_alarm, vm_memory_high_watermark}, State) ->
ok = alert(false, State#alarms.alertees),
- {ok, State#alarms{memory_high_watermark = false}};
+ {ok, State#alarms{vm_memory_high_watermark = false}};
handle_event(_Event, State) ->
{ok, State}.
diff --git a/src/rabbit_memguard.erl b/src/rabbit_memguard.erl
index c543876d..29e21bdc 100644
--- a/src/rabbit_memguard.erl
+++ b/src/rabbit_memguard.erl
@@ -52,13 +52,20 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
--export([update/0]).
+-export([update/0, get_total_memory/1,
+ get_check_interval/0, set_check_interval/1,
+ get_vm_memory_high_watermark/0, set_vm_memory_high_watermark/1]).
-define(SERVER, rabbit_memguard).
-define(DEFAULT_MEMORY_CHECK_INTERVAL, 1000).
--record(state, {memory_limit,
+%% For unknown OS, we assume that we have 512 MB of memory, which is pretty
+%% safe value, even for 32 bit systems. It's better to be slow than to crash.
+-define(MEMORY_SIZE_FOR_UNKNOWN_OS, 512*1024*1024).
+
+-record(state, {total_memory,
+ memory_limit,
timeout,
timer,
alarmed
@@ -73,9 +80,97 @@ update() ->
gen_server2:cast(?SERVER, update).
%%----------------------------------------------------------------------------
-get_system_memory_data(Key) ->
- dict:fetch(Key,
- dict:from_list( memsup:get_system_memory_data() )).
+%% get_total_memory(OS) -> Total
+%% Windows and Freebsd code based on: memsup:get_memory_usage/1
+%% Original code was part of OTP and released under "Erlang Public License".
+
+%% Darwin: Uses vm_stat command.
+get_total_memory({unix,darwin}) ->
+ File = os:cmd("/usr/bin/vm_stat"),
+ Lines = string:tokens(File, "\n"),
+ Dict = dict:from_list(lists:map(fun parse_line_mach/1, Lines)),
+ [PageSize, Inactive, Active, Free, Wired] =
+ [dict:fetch(Key, Dict) ||
+ Key <- [page_size, 'Pages inactive', 'Pages active', 'Pages free',
+ 'Pages wired down']],
+ MemTotal = PageSize * (Inactive + Active + Free + Wired),
+ MemTotal;
+
+%% FreeBSD: Look in /usr/include/sys/vmmeter.h for the format of struct
+%% vmmeter
+get_total_memory({unix,freebsd}) ->
+ PageSize = freebsd_sysctl("vm.stats.vm.v_page_size"),
+ PageCount = freebsd_sysctl("vm.stats.vm.v_page_count"),
+ NMemTotal = PageCount * PageSize,
+ NMemTotal;
+
+%% Win32: Find out how much memory is in use by asking
+%% the os_mon_sysinfo process.
+get_total_memory({win32,_OSname}) ->
+ [Result|_] = os_mon_sysinfo:get_mem_info(),
+ {ok, [_MemLoad, TotPhys, _AvailPhys,
+ _TotPage, _AvailPage, _TotV, _AvailV], _RestStr} =
+ io_lib:fread("~d~d~d~d~d~d~d", Result),
+ TotPhys;
+
+%% Linux: Look in /proc/meminfo
+get_total_memory({unix, linux}) ->
+ File = read_proc_file("/proc/meminfo"),
+ Lines = string:tokens(File, "\n"),
+ Dict = dict:from_list(lists:map(fun parse_line_linux/1, Lines)),
+ MemTotal = dict:fetch('MemTotal', Dict),
+ MemTotal;
+
+get_total_memory(_OsType) ->
+ unknown.
+
+-define(BUFFER_SIZE, 1024).
+
+%% A line looks like "Foo bar: 123456."
+parse_line_mach(Line) ->
+ [Name, RHS | _Rest] = string:tokens(Line, ":"),
+ case Name of
+ "Mach Virtual Memory Statistics" ->
+ ["(page", "size", "of", PageSize, "bytes)"] =
+ string:tokens(RHS, " "),
+ {page_size, list_to_integer(PageSize)};
+ _ ->
+ [Value | _Rest1] = string:tokens(RHS, " ."),
+ {list_to_atom(Name), list_to_integer(Value)}
+ end.
+
+freebsd_sysctl(Def) ->
+ list_to_integer(os:cmd("/sbin/sysctl -n " ++ Def) -- "\n").
+
+%% 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]),
+ Res = read_proc_file(IoDevice, []),
+ file:close(IoDevice),
+ lists:flatten(lists:reverse(Res)).
+
+read_proc_file(IoDevice, Acc) ->
+ case file:read(IoDevice, ?BUFFER_SIZE) of
+ {ok, Res} -> read_proc_file(IoDevice, [Res | Acc]);
+ eof -> Acc
+ end.
+
+%% A line looks like "FooBar: 123456 kB"
+parse_line_linux(Line) ->
+ [Name, RHS | _Rest] = string:tokens(Line, ":"),
+ [Value | UnitsRest] = string:tokens(RHS, " "),
+ Value1 = case UnitsRest of
+ [] -> list_to_integer(Value); %% no units
+ ["kB"] -> list_to_integer(Value) * 1024
+ end,
+ {list_to_atom(Name), Value1}.
+
+
+
+%%----------------------------------------------------------------------------
%% On a 32-bit machine, if you're using more than 2 gigs of RAM
%% you're in big trouble anyway.
@@ -85,29 +180,32 @@ get_vm_limit() ->
8 -> infinity
end.
-
min(A,B) ->
case A<B of
true -> A;
false -> B
end.
-mem_fraction_to_limit(MemFraction) ->
- get_system_memory_data(system_total_memory) * MemFraction.
-
-mem_limit_to_fraction(MemLimit) ->
- MemLimit / get_system_memory_data(system_total_memory).
-get_mem_limit(MemFraction) ->
- min(mem_fraction_to_limit(MemFraction), get_vm_limit()).
+get_mem_limit(MemFraction, TotalMemory) ->
+ min(TotalMemory * MemFraction, get_vm_limit()).
init([MemFraction]) ->
- MemLimit = get_mem_limit(MemFraction),
- rabbit_log:info("Memory alarm set to ~p.~n", [MemLimit]),
- adjust_memsup_interval(?DEFAULT_MEMORY_CHECK_INTERVAL),
+ TotalMemory = case get_total_memory(os:type()) of
+ unknown ->
+ rabbit_log:info("Unknown total memory size for your OS ~p. "
+ "Assuming memory size is ~p bytes.~n",
+ [os:type(), ?MEMORY_SIZE_FOR_UNKNOWN_OS]),
+ ?MEMORY_SIZE_FOR_UNKNOWN_OS;
+ M -> M
+ end,
+ MemLimit = get_mem_limit(MemFraction, TotalMemory),
+ rabbit_log:info("Memory alarm set to ~p, ~p bytes.~n",
+ [MemFraction, MemLimit]),
TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL),
- State = #state { memory_limit = MemLimit,
+ State = #state { total_memory = TotalMemory,
+ memory_limit = MemLimit,
timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL,
timer = TRef,
alarmed = false},
@@ -117,12 +215,27 @@ start_timer(Timeout) ->
{ok, TRef} = timer:apply_interval(Timeout, ?MODULE, update, []),
TRef.
-handle_call(get_memory_high_watermark, _From, State) ->
- {reply, mem_limit_to_fraction(State#state.memory_limit), State};
-handle_call({set_memory_high_watermark, MemFraction}, _From, State) ->
- MemLimit = get_mem_limit(MemFraction),
- rabbit_log:info("Memory alarm set to ~p.~n", [MemLimit]),
+get_check_interval() ->
+ gen_server2:call(?MODULE, get_check_interval).
+
+set_check_interval(Fraction) ->
+ gen_server2:call(?MODULE, {set_check_interval, Fraction}).
+
+get_vm_memory_high_watermark() ->
+ gen_server2:call(?MODULE, get_vm_memory_high_watermark).
+
+set_vm_memory_high_watermark(Fraction) ->
+ gen_server2:call(?MODULE, {set_vm_memory_high_watermark, Fraction}).
+
+
+handle_call(get_vm_memory_high_watermark, _From, State) ->
+ {reply, State#state.memory_limit / State#state.total_memory, State};
+
+handle_call({set_vm_memory_high_watermark, MemFraction}, _From, State) ->
+ MemLimit = get_mem_limit(MemFraction, State#state.total_memory),
+ rabbit_log:info("Memory alarm changed to ~p, ~p bytes.~n",
+ [MemFraction, MemLimit]),
{reply, ok, State#state{memory_limit = MemLimit}};
handle_call(get_check_interval, _From, State) ->
@@ -130,7 +243,6 @@ handle_call(get_check_interval, _From, State) ->
handle_call({set_check_interval, Timeout}, _From, State) ->
{ok, cancel} = timer:cancel(State#state.timer),
- adjust_memsup_interval(Timeout),
{reply, ok, State#state{timeout = Timeout, timer = start_timer(Timeout)}};
handle_call(_Request, _From, State) ->
@@ -152,12 +264,8 @@ code_change(_OldVsn, State, _Extra) ->
{ok, State}.
emit_update_info(State, MemUsed, MemLimit) ->
- {_Total, _Allocated, {_WorstPid, WorstAllocated}}
- = memsup:get_memory_data(),
- FreeMemory = get_system_memory_data(free_memory),
- rabbit_log:info("memory_high_watermark ~p. Memory used:~p allowed:~p "
- "heaviest_process:~p free:~p.~n",
- [State, MemUsed, MemLimit, WorstAllocated, FreeMemory]).
+ rabbit_log:info("vm_memory_high_watermark ~p. Memory used:~p allowed:~p~n",
+ [State, MemUsed, MemLimit]).
internal_update(State = #state { memory_limit = MemLimit,
alarmed = Alarmed}) ->
@@ -166,29 +274,12 @@ internal_update(State = #state { memory_limit = MemLimit,
case {Alarmed, NewAlarmed} of
{false, true} ->
emit_update_info(set, MemUsed, MemLimit),
- alarm_handler:set_alarm({memory_high_watermark, []});
+ alarm_handler:set_alarm({vm_memory_high_watermark, []});
{true, false} ->
emit_update_info(clear, MemUsed, MemLimit),
- alarm_handler:clear_alarm(memory_high_watermark);
+ alarm_handler:clear_alarm(vm_memory_high_watermark);
_ ->
ok
end,
State #state {alarmed = NewAlarmed}.
-adjust_memsup_interval(IntervalMs) ->
- %% 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, IntervalMs},
- infinity).
-