From 08c140a80ca86338fbfcdc88219a1dd1507070a5 Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Wed, 16 Sep 2009 14:42:17 +0100 Subject: new memsup code --- ebin/rabbit_app.in | 2 +- scripts/rabbitmq-server | 4 +- scripts/rabbitmq-server.bat | 6 +- scripts/rabbitmq-service.bat | 6 +- src/rabbit.erl | 15 +++- src/rabbit_alarm.erl | 101 +++------------------- src/rabbit_memguard.erl | 194 +++++++++++++++++++++++++++++++++++++++++++ src/rabbit_memsup.erl | 142 ------------------------------- src/rabbit_memsup_darwin.erl | 88 -------------------- src/rabbit_memsup_linux.erl | 101 ---------------------- 10 files changed, 228 insertions(+), 431 deletions(-) create mode 100644 src/rabbit_memguard.erl delete mode 100644 src/rabbit_memsup.erl delete mode 100644 src/rabbit_memsup_darwin.erl delete mode 100644 src/rabbit_memsup_linux.erl diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 6fc6e464..8796b425 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -22,4 +22,4 @@ {default_pass, <<"guest">>}, {default_vhost, <<"/">>}, {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}, - {memory_alarms, auto}]}]}. + {memory_high_watermark, 0.85}]}]}. diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 547220b4..ffd97b35 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -102,9 +102,9 @@ exec erl \ -sasl sasl_error_logger '{file,"'${RABBITMQ_SASL_LOGS}'"}' \ -os_mon start_cpu_sup true \ -os_mon start_disksup false \ - -os_mon start_memsup false \ + -os_mon start_memsup true \ -os_mon start_os_sup false \ - -os_mon memsup_system_only true \ + -os_mon memsup_system_only false \ -os_mon system_memory_high_watermark 0.95 \ -mnesia dir "\"${RABBITMQ_MNESIA_DIR}\"" \ ${RABBITMQ_CLUSTER_CONFIG_OPTION} \ diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index a784fee3..b83d44ca 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -127,10 +127,10 @@ if exist "%RABBITMQ_EBIN_ROOT%\rabbit.boot" ( -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 false ^ +-os_mon start_memsup true ^ -os_mon start_os_sup false ^ --os_mon memsup_system_only true ^ --os_mon system_memory_high_watermark 0.95 ^ +-os_mon memsup_system_only false ^ +-os_mon system_memory_high_watermark 1.0 ^ -mnesia dir \""%RABBITMQ_MNESIA_DIR%"\" ^ %CLUSTER_CONFIG% ^ %RABBITMQ_SERVER_START_ARGS% ^ diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 29be1742..dcb929b1 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -172,10 +172,10 @@ set ERLANG_SERVICE_ARGUMENTS= ^ -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 false ^ +-os_mon start_memsup true ^ -os_mon start_os_sup false ^ --os_mon memsup_system_only true ^ --os_mon system_memory_high_watermark 0.95 ^ +-os_mon memsup_system_only false ^ +-os_mon system_memory_high_watermark 1.0 ^ -mnesia dir \""%RABBITMQ_MNESIA_DIR%"\" ^ %CLUSTER_CONFIG% ^ %RABBITMQ_SERVER_START_ARGS% ^ diff --git a/src/rabbit.erl b/src/rabbit.erl index ef1e0049..875021aa 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -136,8 +136,12 @@ start(normal, []) -> ok = rabbit_binary_generator: check_empty_content_body_frame_size(), - {ok, MemoryAlarms} = application:get_env(memory_alarms), - ok = rabbit_alarm:start(MemoryAlarms), + ok = rabbit_alarm:start(), + case application:get_env(memory_high_watermark) of + {ok, false} -> ok; + {ok, off} -> ok; + {ok, Float} -> start_child(rabbit_memguard, [Float]) + end, ok = rabbit_amqqueue:start(), @@ -252,6 +256,13 @@ start_child(Mod) -> transient, 100, worker, [Mod]}), ok. +start_child(Mod, Args) -> + {ok,_} = supervisor:start_child(rabbit_sup, + {Mod, {Mod, start_link, 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 309c9a0e..c1997554 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -33,24 +33,19 @@ -behaviour(gen_event). --export([start/1, stop/0, register/2]). +-export([start/0, stop/0, register/2]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). --define(MEMSUP_CHECK_INTERVAL, 1000). - -%% OSes on which we know memory alarms to be trustworthy --define(SUPPORTED_OS, [{unix, linux}, {unix, darwin}]). - --record(alarms, {alertees, system_memory_high_watermark = false}). +-record(alarms, {alertees, memory_high_watermark = false}). %%---------------------------------------------------------------------------- -ifdef(use_specs). -type(mfa_tuple() :: {atom(), atom(), list()}). --spec(start/1 :: (bool() | 'auto') -> 'ok'). +-spec(start/0 :: () -> 'ok'). -spec(stop/0 :: () -> 'ok'). -spec(register/2 :: (pid(), mfa_tuple()) -> 'ok'). @@ -58,20 +53,8 @@ %%---------------------------------------------------------------------------- -start(MemoryAlarms) -> - EnableAlarms = case MemoryAlarms of - true -> true; - false -> false; - auto -> lists:member(os:type(), ?SUPPORTED_OS) - end, - ok = alarm_handler:add_alarm_handler(?MODULE, [EnableAlarms]), - case whereis(memsup) of - undefined -> if EnableAlarms -> ok = start_memsup(), - ok = adjust_memsup_interval(); - true -> ok - end; - _ -> ok = adjust_memsup_interval() - end. +start() -> + ok = alarm_handler:add_alarm_handler(?MODULE, []). stop() -> ok = alarm_handler:delete_alarm_handler(?MODULE). @@ -83,19 +66,13 @@ register(Pid, HighMemMFA) -> %%---------------------------------------------------------------------------- -init([MemoryAlarms]) -> - {ok, #alarms{alertees = case MemoryAlarms of - true -> dict:new(); - false -> undefined - end}}. +init([]) -> + {ok, #alarms{alertees = dict:new()}}. -handle_call({register, _Pid, _HighMemMFA}, - State = #alarms{alertees = undefined}) -> - {ok, ok, State}; handle_call({register, Pid, HighMemMFA}, State = #alarms{alertees = Alertess}) -> _MRef = erlang:monitor(process, Pid), - case State#alarms.system_memory_high_watermark of + case State#alarms.memory_high_watermark of true -> {M, F, A} = HighMemMFA, ok = erlang:apply(M, F, A ++ [Pid, true]); false -> ok @@ -106,20 +83,17 @@ handle_call({register, Pid, HighMemMFA}, handle_call(_Request, State) -> {ok, not_understood, State}. -handle_event({set_alarm, {system_memory_high_watermark, []}}, State) -> +handle_event({set_alarm, {memory_high_watermark, []}}, State) -> ok = alert(true, State#alarms.alertees), - {ok, State#alarms{system_memory_high_watermark = true}}; + {ok, State#alarms{memory_high_watermark = true}}; -handle_event({clear_alarm, system_memory_high_watermark}, State) -> +handle_event({clear_alarm, memory_high_watermark}, State) -> ok = alert(false, State#alarms.alertees), - {ok, State#alarms{system_memory_high_watermark = false}}; + {ok, State#alarms{memory_high_watermark = false}}; handle_event(_Event, State) -> {ok, State}. -handle_info({'DOWN', _MRef, process, _Pid, _Reason}, - State = #alarms{alertees = undefined}) -> - {ok, State}; handle_info({'DOWN', _MRef, process, Pid, _Reason}, State = #alarms{alertees = Alertess}) -> {ok, State#alarms{alertees = dict:erase(Pid, Alertess)}}; @@ -134,57 +108,6 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. %%---------------------------------------------------------------------------- - -start_memsup() -> - {Mod, Args} = - case os:type() of - %% 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. - %% - {unix, linux} -> {rabbit_memsup, [rabbit_memsup_linux]}; - {unix, darwin} -> {rabbit_memsup, [rabbit_memsup_darwin]}; - - %% Start memsup programmatically rather than via the - %% rabbitmq-server script. This is not quite the right - %% thing to do as os_mon checks to see if memsup is - %% available before starting it, but as memsup is - %% available everywhere (even on VXWorks) it should be - %% ok. - %% - %% One benefit of the programmatic startup is that we - %% can add our alarm_handler before memsup is running, - %% thus ensuring that we notice memory alarms that go - %% off on startup. - %% - _ -> {memsup, []} - end, - %% This is based on os_mon:childspec(memsup, true) - {ok, _} = supervisor:start_child( - os_mon_sup, - {memsup, {Mod, start_link, Args}, - permanent, 2000, worker, [Mod]}), - ok. - -adjust_memsup_interval() -> - %% 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). - alert(_Alert, undefined) -> ok; alert(Alert, Alertees) -> diff --git a/src/rabbit_memguard.erl b/src/rabbit_memguard.erl new file mode 100644 index 00000000..c543876d --- /dev/null +++ b/src/rabbit_memguard.erl @@ -0,0 +1,194 @@ +%% 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 before 22-Nov-2008 00:00:00 GMT by LShift Ltd, +%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd +%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial +%% Technologies LLC, and Rabbit Technologies Ltd. +%% +%% Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift +%% Ltd. Portions created by Cohesive Financial Technologies LLC are +%% Copyright (C) 2007-2009 Cohesive Financial Technologies +%% LLC. Portions created by Rabbit Technologies Ltd are Copyright +%% (C) 2007-2009 Rabbit Technologies Ltd. +%% +%% All Rights Reserved. +%% +%% Contributor(s): ______________________________________. +%% + +%% In practice erlang shouldn't be allowed to grow to more than a half +%% of available memory. The pessimistic scenario is when erlang VM has +%% a single erlang process that's consuming all the memory. +%% In such case during garbage collection erlang tries to allocate +%% huge chunk of continuous memory, which can result in a crash +%% (likely on 32-bit machine) or heavy swapping (likely on 64-bit). +%% +%% This module tries to warn Rabbit before such situations happen, +%% so that it has higher chances to prevent running out of memory. +%% +%% This code depends on Erlang Memsup supervisor. Setting the update interval +%% causes a side effect of setting the interval on Memsup. +%% This should rarely be an issue. + +-module(rabbit_memguard). + +-behaviour(gen_server2). + +-export([start_link/1]). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-export([update/0]). + + +-define(SERVER, rabbit_memguard). +-define(DEFAULT_MEMORY_CHECK_INTERVAL, 1000). + +-record(state, {memory_limit, + timeout, + timer, + alarmed + }). + +%%---------------------------------------------------------------------------- + +start_link(Args) -> + gen_server2:start_link({local, ?SERVER}, ?MODULE, [Args], []). + +update() -> + gen_server2:cast(?SERVER, update). + +%%---------------------------------------------------------------------------- +get_system_memory_data(Key) -> + dict:fetch(Key, + dict:from_list( memsup:get_system_memory_data() )). + +%% On a 32-bit machine, if you're using more than 2 gigs of RAM +%% you're in big trouble anyway. +get_vm_limit() -> + case erlang:system_info(wordsize) of + 4 -> 2*1024*1024*1024; + 8 -> infinity + end. + + +min(A,B) -> + case A 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()). + +init([MemFraction]) -> + MemLimit = get_mem_limit(MemFraction), + rabbit_log:info("Memory alarm set to ~p.~n", [MemLimit]), + adjust_memsup_interval(?DEFAULT_MEMORY_CHECK_INTERVAL), + TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL), + State = #state { memory_limit = MemLimit, + timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, + timer = TRef, + alarmed = false}, + {ok, internal_update(State)}. + +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]), + {reply, ok, State#state{memory_limit = MemLimit}}; + +handle_call(get_check_interval, _From, State) -> + {reply, State#state.timeout, 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) -> + {noreply, State}. + +handle_cast(update, State) -> + {noreply, internal_update(State)}; + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +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]). + +internal_update(State = #state { memory_limit = MemLimit, + alarmed = Alarmed}) -> + MemUsed = erlang:memory(total), + NewAlarmed = MemUsed > MemLimit, + case {Alarmed, NewAlarmed} of + {false, true} -> + emit_update_info(set, MemUsed, MemLimit), + alarm_handler:set_alarm({memory_high_watermark, []}); + {true, false} -> + emit_update_info(clear, MemUsed, MemLimit), + alarm_handler:clear_alarm(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). + diff --git a/src/rabbit_memsup.erl b/src/rabbit_memsup.erl deleted file mode 100644 index b0d57cb2..00000000 --- a/src/rabbit_memsup.erl +++ /dev/null @@ -1,142 +0,0 @@ -%% 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 before 22-Nov-2008 00:00:00 GMT by LShift Ltd, -%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd -%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial -%% Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift -%% Ltd. Portions created by Cohesive Financial Technologies LLC are -%% Copyright (C) 2007-2009 Cohesive Financial Technologies -%% LLC. Portions created by Rabbit Technologies Ltd are Copyright -%% (C) 2007-2009 Rabbit Technologies Ltd. -%% -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. -%% - --module(rabbit_memsup). - --behaviour(gen_server). - --export([start_link/1]). - --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --export([update/0]). - --record(state, {memory_fraction, - timeout, - timer, - mod, - mod_state, - alarmed - }). - --define(SERVER, memsup). %% must be the same as the standard memsup - --define(DEFAULT_MEMORY_CHECK_INTERVAL, 1000). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(start_link/1 :: (atom()) -> {'ok', pid()} | 'ignore' | {'error', any()}). --spec(update/0 :: () -> 'ok'). - --endif. - -%%---------------------------------------------------------------------------- - -start_link(Args) -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [Args], []). - -update() -> - gen_server:cast(?SERVER, update). - -%%---------------------------------------------------------------------------- - -init([Mod]) -> - Fraction = os_mon:get_env(memsup, system_memory_high_watermark), - TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL), - InitState = Mod:init(), - State = #state { memory_fraction = Fraction, - timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, - timer = TRef, - mod = Mod, - mod_state = InitState, - alarmed = false }, - {ok, internal_update(State)}. - -start_timer(Timeout) -> - {ok, TRef} = timer:apply_interval(Timeout, ?MODULE, update, []), - TRef. - -%% Export the same API as the real memsup. Note that -%% get_sysmem_high_watermark gives an int in the range 0 - 100, while -%% set_sysmem_high_watermark takes a float in the range 0.0 - 1.0. -handle_call(get_sysmem_high_watermark, _From, State) -> - {reply, trunc(100 * State#state.memory_fraction), State}; - -handle_call({set_sysmem_high_watermark, Float}, _From, State) -> - {reply, ok, State#state{memory_fraction = Float}}; - -handle_call(get_check_interval, _From, State) -> - {reply, State#state.timeout, State}; - -handle_call({set_check_interval, Timeout}, _From, State) -> - {ok, cancel} = timer:cancel(State#state.timer), - {reply, ok, State#state{timeout = Timeout, timer = start_timer(Timeout)}}; - -handle_call(get_memory_data, _From, - State = #state { mod = Mod, mod_state = ModState }) -> - {reply, Mod:get_memory_data(ModState), State}; - -handle_call(_Request, _From, State) -> - {noreply, State}. - -handle_cast(update, State) -> - {noreply, internal_update(State)}; - -handle_cast(_Request, State) -> - {noreply, State}. - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -internal_update(State = #state { memory_fraction = MemoryFraction, - alarmed = Alarmed, - mod = Mod, mod_state = ModState }) -> - ModState1 = Mod:update(ModState), - {MemTotal, MemUsed, _BigProc} = Mod:get_memory_data(ModState1), - NewAlarmed = MemUsed / MemTotal > MemoryFraction, - case {Alarmed, NewAlarmed} of - {false, true} -> - alarm_handler:set_alarm({system_memory_high_watermark, []}); - {true, false} -> - alarm_handler:clear_alarm(system_memory_high_watermark); - _ -> - ok - end, - State #state { mod_state = ModState1, alarmed = NewAlarmed }. diff --git a/src/rabbit_memsup_darwin.erl b/src/rabbit_memsup_darwin.erl deleted file mode 100644 index 3de2d843..00000000 --- a/src/rabbit_memsup_darwin.erl +++ /dev/null @@ -1,88 +0,0 @@ -%% 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 before 22-Nov-2008 00:00:00 GMT by LShift Ltd, -%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd -%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial -%% Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift -%% Ltd. Portions created by Cohesive Financial Technologies LLC are -%% Copyright (C) 2007-2009 Cohesive Financial Technologies -%% LLC. Portions created by Rabbit Technologies Ltd are Copyright -%% (C) 2007-2009 Rabbit Technologies Ltd. -%% -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. -%% - --module(rabbit_memsup_darwin). - --export([init/0, update/1, get_memory_data/1]). - --record(state, {total_memory, - allocated_memory}). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --type(state() :: #state { total_memory :: ('undefined' | non_neg_integer()), - allocated_memory :: ('undefined' | non_neg_integer()) - }). - --spec(init/0 :: () -> state()). --spec(update/1 :: (state()) -> state()). --spec(get_memory_data/1 :: (state()) -> {non_neg_integer(), non_neg_integer(), - ('undefined' | pid())}). - --endif. - -%%---------------------------------------------------------------------------- - -init() -> - #state{total_memory = undefined, - allocated_memory = undefined}. - -update(State) -> - File = os:cmd("/usr/bin/vm_stat"), - Lines = string:tokens(File, "\n"), - Dict = dict:from_list(lists:map(fun parse_line/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), - MemUsed = PageSize * (Active + Wired), - State#state{total_memory = MemTotal, allocated_memory = MemUsed}. - -get_memory_data(State) -> - {State#state.total_memory, State#state.allocated_memory, undefined}. - -%%---------------------------------------------------------------------------- - -%% A line looks like "Foo bar: 123456." -parse_line(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. diff --git a/src/rabbit_memsup_linux.erl b/src/rabbit_memsup_linux.erl deleted file mode 100644 index ca942d7c..00000000 --- a/src/rabbit_memsup_linux.erl +++ /dev/null @@ -1,101 +0,0 @@ -%% 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 before 22-Nov-2008 00:00:00 GMT by LShift Ltd, -%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd -%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial -%% Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift -%% Ltd. Portions created by Cohesive Financial Technologies LLC are -%% Copyright (C) 2007-2009 Cohesive Financial Technologies -%% LLC. Portions created by Rabbit Technologies Ltd are Copyright -%% (C) 2007-2009 Rabbit Technologies Ltd. -%% -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. -%% - --module(rabbit_memsup_linux). - --export([init/0, update/1, get_memory_data/1]). - --record(state, {total_memory, - allocated_memory}). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --type(state() :: #state { total_memory :: ('undefined' | non_neg_integer()), - allocated_memory :: ('undefined' | non_neg_integer()) - }). - --spec(init/0 :: () -> state()). --spec(update/1 :: (state()) -> state()). --spec(get_memory_data/1 :: (state()) -> {non_neg_integer(), non_neg_integer(), - ('undefined' | pid())}). - --endif. - -%%---------------------------------------------------------------------------- - -init() -> - #state{total_memory = undefined, - allocated_memory = undefined}. - -update(State) -> - File = read_proc_file("/proc/meminfo"), - Lines = string:tokens(File, "\n"), - Dict = dict:from_list(lists:map(fun parse_line/1, Lines)), - [MemTotal, MemFree, Buffers, Cached] = - [dict:fetch(Key, Dict) || - Key <- ['MemTotal', 'MemFree', 'Buffers', 'Cached']], - MemUsed = MemTotal - MemFree - Buffers - Cached, - State#state{total_memory = MemTotal, allocated_memory = MemUsed}. - -get_memory_data(State) -> - {State#state.total_memory, State#state.allocated_memory, undefined}. - -%%---------------------------------------------------------------------------- - --define(BUFFER_SIZE, 1024). - -%% 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(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}. -- cgit v1.2.1 From a7a34f7bc76bb4b4cb1e37adfeb4a41e31381294 Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Fri, 25 Sep 2009 17:31:13 +0100 Subject: qa fixes, some refactoring --- ebin/rabbit_app.in | 3 +- scripts/rabbitmq-server | 8 +- scripts/rabbitmq-server.bat | 8 +- scripts/rabbitmq-service.bat | 8 +- src/rabbit.erl | 10 +-- src/rabbit_alarm.erl | 12 +-- src/rabbit_memguard.erl | 187 ++++++++++++++++++++++++++++++++----------- 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 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). - -- cgit v1.2.1 From 9f47dc06410c0a6cf5f51c1e988458357d15149a Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Fri, 25 Sep 2009 17:45:27 +0100 Subject: renamed to vm_memory_monitor --- src/rabbit.erl | 2 +- src/rabbit_memguard.erl | 285 ---------------------------------------------- src/vm_memory_monitor.erl | 285 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+), 286 deletions(-) delete mode 100644 src/rabbit_memguard.erl create mode 100644 src/vm_memory_monitor.erl diff --git a/src/rabbit.erl b/src/rabbit.erl index 7df187cf..f19facfd 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -142,7 +142,7 @@ start(normal, []) -> case MemoryWatermark of {ok, false} -> ok; {ok, off} -> ok; - {ok, Float} -> start_child(rabbit_memguard, [Float]) + {ok, Float} -> start_child(vm_memory_monitor, [Float]) end, ok = rabbit_amqqueue:start(), diff --git a/src/rabbit_memguard.erl b/src/rabbit_memguard.erl deleted file mode 100644 index 29e21bdc..00000000 --- a/src/rabbit_memguard.erl +++ /dev/null @@ -1,285 +0,0 @@ -%% 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 before 22-Nov-2008 00:00:00 GMT by LShift Ltd, -%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd -%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial -%% Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift -%% Ltd. Portions created by Cohesive Financial Technologies LLC are -%% Copyright (C) 2007-2009 Cohesive Financial Technologies -%% LLC. Portions created by Rabbit Technologies Ltd are Copyright -%% (C) 2007-2009 Rabbit Technologies Ltd. -%% -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. -%% - -%% In practice erlang shouldn't be allowed to grow to more than a half -%% of available memory. The pessimistic scenario is when erlang VM has -%% a single erlang process that's consuming all the memory. -%% In such case during garbage collection erlang tries to allocate -%% huge chunk of continuous memory, which can result in a crash -%% (likely on 32-bit machine) or heavy swapping (likely on 64-bit). -%% -%% This module tries to warn Rabbit before such situations happen, -%% so that it has higher chances to prevent running out of memory. -%% -%% This code depends on Erlang Memsup supervisor. Setting the update interval -%% causes a side effect of setting the interval on Memsup. -%% This should rarely be an issue. - --module(rabbit_memguard). - --behaviour(gen_server2). - --export([start_link/1]). - --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --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). - -%% 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 - }). - -%%---------------------------------------------------------------------------- - -start_link(Args) -> - gen_server2:start_link({local, ?SERVER}, ?MODULE, [Args], []). - -update() -> - gen_server2:cast(?SERVER, update). - -%%---------------------------------------------------------------------------- -%% 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. -get_vm_limit() -> - case erlang:system_info(wordsize) of - 4 -> 2*1024*1024*1024; - 8 -> infinity - end. - -min(A,B) -> - case A A; - false -> B - end. - - - -get_mem_limit(MemFraction, TotalMemory) -> - min(TotalMemory * MemFraction, get_vm_limit()). - -init([MemFraction]) -> - 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 { total_memory = TotalMemory, - memory_limit = MemLimit, - timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, - timer = TRef, - alarmed = false}, - {ok, internal_update(State)}. - -start_timer(Timeout) -> - {ok, TRef} = timer:apply_interval(Timeout, ?MODULE, update, []), - TRef. - - -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) -> - {reply, State#state.timeout, State}; - -handle_call({set_check_interval, Timeout}, _From, State) -> - {ok, cancel} = timer:cancel(State#state.timer), - {reply, ok, State#state{timeout = Timeout, timer = start_timer(Timeout)}}; - -handle_call(_Request, _From, State) -> - {noreply, State}. - -handle_cast(update, State) -> - {noreply, internal_update(State)}; - -handle_cast(_Request, State) -> - {noreply, State}. - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -emit_update_info(State, MemUsed, MemLimit) -> - 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}) -> - MemUsed = erlang:memory(total), - NewAlarmed = MemUsed > MemLimit, - case {Alarmed, NewAlarmed} of - {false, true} -> - emit_update_info(set, MemUsed, MemLimit), - alarm_handler:set_alarm({vm_memory_high_watermark, []}); - {true, false} -> - emit_update_info(clear, MemUsed, MemLimit), - alarm_handler:clear_alarm(vm_memory_high_watermark); - _ -> - ok - end, - State #state {alarmed = NewAlarmed}. - diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl new file mode 100644 index 00000000..2d10f069 --- /dev/null +++ b/src/vm_memory_monitor.erl @@ -0,0 +1,285 @@ +%% 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 before 22-Nov-2008 00:00:00 GMT by LShift Ltd, +%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd +%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial +%% Technologies LLC, and Rabbit Technologies Ltd. +%% +%% Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift +%% Ltd. Portions created by Cohesive Financial Technologies LLC are +%% Copyright (C) 2007-2009 Cohesive Financial Technologies +%% LLC. Portions created by Rabbit Technologies Ltd are Copyright +%% (C) 2007-2009 Rabbit Technologies Ltd. +%% +%% All Rights Reserved. +%% +%% Contributor(s): ______________________________________. +%% + +%% In practice erlang shouldn't be allowed to grow to more than a half +%% of available memory. The pessimistic scenario is when erlang VM has +%% a single erlang process that's consuming all the memory. +%% In such case during garbage collection erlang tries to allocate +%% huge chunk of continuous memory, which can result in a crash +%% (likely on 32-bit machine) or heavy swapping (likely on 64-bit). +%% +%% This module tries to warn Rabbit before such situations happen, +%% so that it has higher chances to prevent running out of memory. +%% +%% This code depends on Erlang Memsup supervisor. Setting the update interval +%% causes a side effect of setting the interval on Memsup. +%% This should rarely be an issue. + +-module(vm_memory_monitor). + +-behaviour(gen_server2). + +-export([start_link/1]). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-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, ?MODULE). +-define(DEFAULT_MEMORY_CHECK_INTERVAL, 1000). + +%% 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 + }). + +%%---------------------------------------------------------------------------- + +start_link(Args) -> + gen_server2:start_link({local, ?SERVER}, ?MODULE, [Args], []). + +update() -> + gen_server2:cast(?SERVER, update). + +%%---------------------------------------------------------------------------- +%% 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. +get_vm_limit() -> + case erlang:system_info(wordsize) of + 4 -> 2*1024*1024*1024; + 8 -> infinity + end. + +min(A,B) -> + case A A; + false -> B + end. + + + +get_mem_limit(MemFraction, TotalMemory) -> + min(TotalMemory * MemFraction, get_vm_limit()). + +init([MemFraction]) -> + 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 { total_memory = TotalMemory, + memory_limit = MemLimit, + timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, + timer = TRef, + alarmed = false}, + {ok, internal_update(State)}. + +start_timer(Timeout) -> + {ok, TRef} = timer:apply_interval(Timeout, ?MODULE, update, []), + TRef. + + +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) -> + {reply, State#state.timeout, State}; + +handle_call({set_check_interval, Timeout}, _From, State) -> + {ok, cancel} = timer:cancel(State#state.timer), + {reply, ok, State#state{timeout = Timeout, timer = start_timer(Timeout)}}; + +handle_call(_Request, _From, State) -> + {noreply, State}. + +handle_cast(update, State) -> + {noreply, internal_update(State)}; + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +emit_update_info(State, MemUsed, MemLimit) -> + 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}) -> + MemUsed = erlang:memory(total), + NewAlarmed = MemUsed > MemLimit, + case {Alarmed, NewAlarmed} of + {false, true} -> + emit_update_info(set, MemUsed, MemLimit), + alarm_handler:set_alarm({vm_memory_high_watermark, []}); + {true, false} -> + emit_update_info(clear, MemUsed, MemLimit), + alarm_handler:clear_alarm(vm_memory_high_watermark); + _ -> + ok + end, + State #state {alarmed = NewAlarmed}. + -- cgit v1.2.1 From 66fd625d73749a5371daf50c927be564b99ff49d Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Mon, 28 Sep 2009 12:44:07 +0100 Subject: minor os_mon fixes --- scripts/rabbitmq-server | 4 +++- scripts/rabbitmq-server.bat | 2 ++ scripts/rabbitmq-service.bat | 2 ++ src/rabbit.erl | 3 ++- src/vm_memory_monitor.erl | 3 +-- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index dcc23f79..89537ddf 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -100,8 +100,10 @@ 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 false \ - -os_mon system_memory_high_watermark 0.70 \ + -os_mon system_memory_high_watermark 0.7 \ -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 3f5ac3e7..6439baeb 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -125,6 +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 false ^ -os_mon system_memory_high_watermark 0.7 ^ -mnesia dir \""%RABBITMQ_MNESIA_DIR%"\" ^ diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 480acdda..09c5993e 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -170,6 +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 false ^ -os_mon system_memory_high_watermark 0.7 ^ -mnesia dir \""%RABBITMQ_MNESIA_DIR%"\" ^ diff --git a/src/rabbit.erl b/src/rabbit.erl index f19facfd..d7a9497c 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -142,7 +142,8 @@ start(normal, []) -> case MemoryWatermark of {ok, false} -> ok; {ok, off} -> ok; - {ok, Float} -> start_child(vm_memory_monitor, [Float]) + {ok, Float} -> start_child(vm_memory_monitor, [Float]); + undefined -> throw({undefined, os_mon, system_memory_high_watermark, settings}) end, ok = rabbit_amqqueue:start(), diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 2d10f069..cdd53236 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -124,8 +124,6 @@ get_total_memory({unix, linux}) -> 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, ":"), @@ -152,6 +150,7 @@ read_proc_file(File) -> file:close(IoDevice), lists:flatten(lists:reverse(Res)). +-define(BUFFER_SIZE, 1024). read_proc_file(IoDevice, Acc) -> case file:read(IoDevice, ?BUFFER_SIZE) of {ok, Res} -> read_proc_file(IoDevice, [Res | Acc]); -- cgit v1.2.1 From bb701c9574cfe868c360fe997fb22e9a3c339a80 Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Mon, 12 Oct 2009 12:23:17 +0100 Subject: QA fixes --- src/vm_memory_monitor.erl | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index cdd53236..5c97664b 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -175,20 +175,14 @@ parse_line_linux(Line) -> %% you're in big trouble anyway. get_vm_limit() -> case erlang:system_info(wordsize) of - 4 -> 2*1024*1024*1024; - 8 -> infinity + 4 -> 2*1024*1024*1024; % 2 GiB for 32 bits + 8 -> 64*1024*1024*1024*1024 % 64 TiB for 64 bits end. -min(A,B) -> - case A A; - false -> B - end. - - get_mem_limit(MemFraction, TotalMemory) -> - min(TotalMemory * MemFraction, get_vm_limit()). + lists:min([erlang:trunc(TotalMemory * MemFraction), + get_vm_limit()]). init([MemFraction]) -> TotalMemory = case get_total_memory(os:type()) of @@ -200,8 +194,8 @@ init([MemFraction]) -> M -> M end, MemLimit = get_mem_limit(MemFraction, TotalMemory), - rabbit_log:info("Memory alarm set to ~p, ~p bytes.~n", - [MemFraction, MemLimit]), + rabbit_log:info("Memory limit set to ~pMiB.~n", + [erlang:trunc(MemLimit/1048576)]), TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL), State = #state { total_memory = TotalMemory, memory_limit = MemLimit, -- cgit v1.2.1 From 9f0a43ea48b5d92efe771defa07dd26d3ce159a5 Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Mon, 12 Oct 2009 13:25:20 +0100 Subject: QA: erlang:trunc -> trunc --- src/vm_memory_monitor.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 5c97664b..b9320f5b 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -181,7 +181,7 @@ get_vm_limit() -> get_mem_limit(MemFraction, TotalMemory) -> - lists:min([erlang:trunc(TotalMemory * MemFraction), + lists:min([trunc(TotalMemory * MemFraction), get_vm_limit()]). init([MemFraction]) -> @@ -195,7 +195,7 @@ init([MemFraction]) -> end, MemLimit = get_mem_limit(MemFraction, TotalMemory), rabbit_log:info("Memory limit set to ~pMiB.~n", - [erlang:trunc(MemLimit/1048576)]), + [trunc(MemLimit/1048576)]), TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL), State = #state { total_memory = TotalMemory, memory_limit = MemLimit, -- cgit v1.2.1