From 589e6181c4e0d4ffe4ec91685e6e6ee318ff4e4a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 24 Oct 2012 17:32:09 +0100 Subject: re-order record definitions so they match the OTP supervisor --- src/supervisor2.erl | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 5af38573..a4e21e47 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -85,6 +85,20 @@ -export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). -export([handle_cast/2]). +%%-------------------------------------------------------------------------- +%% Records - here we differ from supervisor.erl in that we do not +%% embed type specifications directly in our records, so that -D use_specs +%% can be used to turn this off for older versions of Erlang +%%-------------------------------------------------------------------------- + +-record(child, {pid = undefined, % pid is undefined when child is not running + name, + mfa, + restart_type, + shutdown, + child_type, + modules = []}). + -define(DICT, dict). -record(state, {name, @@ -97,14 +111,6 @@ module, args}). --record(child, {pid = undefined, % pid is undefined when child is not running - name, - mfa, - restart_type, - shutdown, - child_type, - modules = []}). - -define(is_simple(State), State#state.strategy =:= simple_one_for_one orelse State#state.strategy =:= simple_one_for_one_terminate). -define(is_terminate_simple(State), -- cgit v1.2.1 From 481f29b1d5e98568de58e007dfd8b6c075cc74e0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 26 Oct 2012 10:30:59 +0100 Subject: re-order things a bit and pretend use_specs doesn't exist for now - closer diff with otp supervisor --- src/supervisor2.erl | 209 +++++++++++++++++++++++----------------------------- 1 file changed, 94 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index a4e21e47..99c41aa6 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -86,44 +86,11 @@ -export([handle_cast/2]). %%-------------------------------------------------------------------------- -%% Records - here we differ from supervisor.erl in that we do not -%% embed type specifications directly in our records, so that -D use_specs -%% can be used to turn this off for older versions of Erlang -%%-------------------------------------------------------------------------- - --record(child, {pid = undefined, % pid is undefined when child is not running - name, - mfa, - restart_type, - shutdown, - child_type, - modules = []}). --define(DICT, dict). - --record(state, {name, - strategy, - children = [], - dynamics = ?DICT:new(), - intensity, - period, - restarts = [], - module, - args}). +-export_type([child_spec/0, startchild_ret/0, strategy/0]). --define(is_simple(State), State#state.strategy =:= simple_one_for_one orelse - State#state.strategy =:= simple_one_for_one_terminate). --define(is_terminate_simple(State), - State#state.strategy =:= simple_one_for_one_terminate). - --ifdef(use_specs). - -%%-------------------------------------------------------------------------- -%% Types %%-------------------------------------------------------------------------- --export_type([child_spec/0, startchild_ret/0, strategy/0, sup_name/0]). - -type child() :: 'undefined' | pid(). -type child_id() :: term(). -type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. @@ -146,11 +113,19 @@ Type :: worker(), Modules :: modules()}. - -type strategy() :: 'one_for_all' | 'one_for_one' | 'rest_for_one' | 'simple_one_for_one' | 'simple_one_for_one_terminate'. +%%-------------------------------------------------------------------------- + +-record(child, {pid = undefined, % pid is undefined when child is not running + name, + mfa, + restart_type, + shutdown, + child_type, + modules = []}). -type child_rec() :: #child{pid :: child() | {restarting,pid()} | [pid()], name :: child_id(), mfa :: mfargs(), @@ -159,15 +134,28 @@ child_type :: worker(), modules :: modules()}. +-define(DICT, dict). + +-record(state, {name, + strategy, + children = [], + dynamics = ?DICT:new(), + intensity, + period, + restarts = [], + module, + args}). -type state() :: #state{strategy :: strategy(), children :: [child_rec()], dynamics :: ?DICT(), intensity :: non_neg_integer(), period :: pos_integer()}. -%%-------------------------------------------------------------------------- -%% Callback behaviour -%%-------------------------------------------------------------------------- +-define(is_simple(State), State#state.strategy =:= simple_one_for_one orelse + State#state.strategy =:= simple_one_for_one_terminate). + +-define(is_terminate_simple(State), + State#state.strategy =:= simple_one_for_one_terminate). -callback init(Args :: term()) -> {ok, {{RestartStrategy :: strategy(), @@ -176,9 +164,21 @@ [ChildSpec :: child_spec()]}} | ignore. -%%-------------------------------------------------------------------------- -%% Specs -%%-------------------------------------------------------------------------- +%%% --------------------------------------------------- +%%% This is a general process supervisor built upon gen_server.erl. +%%% Servers/processes should/could also be built using gen_server.erl. +%%% SupName = {local, atom()} | {global, atom()}. +%%% --------------------------------------------------- + +start_link(Mod, Args) -> + gen_server:start_link(?MODULE, {self, Mod, Args}, []). + +start_link(SupName, Mod, Args) -> + gen_server:start_link(SupName, ?MODULE, {SupName, Mod, Args}, []). + +%%% --------------------------------------------------- +%%% Interface functions. +%%% --------------------------------------------------- -type startchild_err() :: 'already_present' | {'already_started', Child :: child()} | term(). @@ -189,6 +189,8 @@ -spec start_child(SupRef, ChildSpec) -> startchild_ret() when SupRef :: sup_ref(), ChildSpec :: child_spec() | (List :: [term()]). +start_child(Supervisor, ChildSpec) -> + call(Supervisor, {start_child, ChildSpec}). -spec restart_child(SupRef, Id) -> Result when SupRef :: sup_ref(), @@ -197,18 +199,32 @@ | {'ok', Child :: child(), Info :: term()} | {'error', Error}, Error :: 'running' | 'not_found' | 'simple_one_for_one' | term(). +restart_child(Supervisor, Name) -> + call(Supervisor, {restart_child, Name}). -spec delete_child(SupRef, Id) -> Result when SupRef :: sup_ref(), Id :: child_id(), Result :: 'ok' | {'error', Error}, Error :: 'running' | 'not_found' | 'simple_one_for_one'. +delete_child(Supervisor, Name) -> + call(Supervisor, {delete_child, Name}). + +%%----------------------------------------------------------------- +%% Func: terminate_child/2 +%% Returns: ok | {error, Reason} +%% Note that the child is *always* terminated in some +%% way (maybe killed). +%%----------------------------------------------------------------- -spec terminate_child(SupRef, Id) -> Result when SupRef :: sup_ref(), + Id :: pid() | child_id(), Result :: 'ok' | {'error', Error}, Error :: 'not_found' | 'simple_one_for_one'. +terminate_child(Supervisor, Name) -> + call(Supervisor, {terminate_child, Name}). -spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when SupRef :: sup_ref(), @@ -216,86 +232,16 @@ Child :: child(), Type :: worker(), Modules :: modules(). - --spec check_childspecs(ChildSpecs) -> Result when - ChildSpecs :: [child_spec()], - Result :: 'ok' | {'error', Error :: term()}. - --type init_sup_name() :: sup_name() | 'self'. - --type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}} - | {'bad_start_spec', term()} | {'start_spec', term()} - | {'supervisor_data', term()}. - --spec init({init_sup_name(), module(), [term()]}) -> - {'ok', state()} | 'ignore' | {'stop', stop_rsn()}. - --type call() :: 'which_children'. --spec handle_call(call(), term(), state()) -> {'reply', term(), state()}. - --spec handle_cast('null', state()) -> {'noreply', state()}. - --spec handle_info(term(), state()) -> - {'noreply', state()} | {'stop', 'shutdown', state()}. - --spec terminate(term(), state()) -> 'ok'. - --spec code_change(term(), state(), term()) -> - {'ok', state()} | {'error', term()}. - --else. - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{init,1}]; -behaviour_info(_Other) -> - undefined. - --endif. - -%%% --------------------------------------------------- -%%% This is a general process supervisor built upon gen_server.erl. -%%% Servers/processes should/could also be built using gen_server.erl. -%%% SupName = {local, atom()} | {global, atom()}. -%%% --------------------------------------------------- -start_link(Mod, Args) -> - gen_server:start_link(?MODULE, {self, Mod, Args}, []). - -start_link(SupName, Mod, Args) -> - gen_server:start_link(SupName, ?MODULE, {SupName, Mod, Args}, []). - -%%% --------------------------------------------------- -%%% Interface functions. -%%% --------------------------------------------------- -start_child(Supervisor, ChildSpec) -> - call(Supervisor, {start_child, ChildSpec}). - -restart_child(Supervisor, Name) -> - call(Supervisor, {restart_child, Name}). - -delete_child(Supervisor, Name) -> - call(Supervisor, {delete_child, Name}). - -%%----------------------------------------------------------------- -%% Func: terminate_child/2 -%% Returns: ok | {error, Reason} -%% Note that the child is *always* terminated in some -%% way (maybe killed). -%%----------------------------------------------------------------- -terminate_child(Supervisor, Name) -> - call(Supervisor, {terminate_child, Name}). - which_children(Supervisor) -> call(Supervisor, which_children). -find_child(Supervisor, Name) -> - [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), - Name1 =:= Name]. - call(Supervisor, Req) -> gen_server:call(Supervisor, Req, infinity). +-spec check_childspecs(ChildSpecs) -> Result when + ChildSpecs :: [child_spec()], + Result :: 'ok' | {'error', Error :: term()}. + check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> case check_startspec(ChildSpecs) of {ok, _} -> ok; @@ -303,11 +249,32 @@ check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> end; check_childspecs(X) -> {error, {badarg, X}}. +find_child(Supervisor, Name) -> + [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), + Name1 =:= Name]. + +%-export([behaviour_info/1]). + +%behaviour_info(callbacks) -> +% [{init,1}]; +%behaviour_info(_Other) -> +% undefined. + %%% --------------------------------------------------- %%% %%% Initialize the supervisor. %%% %%% --------------------------------------------------- + +-type init_sup_name() :: sup_name() | 'self'. + +-type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}} + | {'bad_start_spec', term()} | {'start_spec', term()} + | {'supervisor_data', term()}. + +-spec init({init_sup_name(), module(), [term()]}) -> + {'ok', state()} | 'ignore' | {'stop', stop_rsn()}. + init({SupName, Mod, Args}) -> process_flag(trap_exit, true), case Mod:init(Args) of @@ -413,6 +380,9 @@ do_start_child_i(M, F, A) -> %%% Callback functions. %%% %%% --------------------------------------------------- +-type call() :: 'which_children'. +-spec handle_call(call(), term(), state()) -> {'reply', term(), state()}. + handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> #child{mfa = {M, F, A}} = hd(State#state.children), Args = A ++ EArgs, @@ -500,6 +470,7 @@ handle_call(which_children, _From, State) -> State#state.children), {reply, Resp, State}. +-spec handle_cast('null', state()) -> {'noreply', state()}. %%% Hopefully cause a function-clause as there is no API function %%% that utilizes cast. handle_cast(null, State) -> @@ -508,6 +479,9 @@ handle_cast(null, State) -> {noreply, State}. +-spec handle_info(term(), state()) -> + {'noreply', state()} | {'stop', 'shutdown', state()}. + handle_info({delayed_restart, {RestartType, Reason, Child}}, State) when ?is_simple(State) -> {ok, NState} = do_restart(RestartType, Reason, Child, State), @@ -539,6 +513,8 @@ handle_info(Msg, State) -> %% %% Terminate this server. %% +-spec terminate(term(), state()) -> 'ok'. + terminate(_Reason, State) when ?is_terminate_simple(State) -> terminate_simple_children( hd(State#state.children), State#state.dynamics, State#state.name), @@ -556,6 +532,9 @@ terminate(_Reason, State) -> %% NOTE: This requires that the init function of the call-back module %% does not have any side effects. %% +-spec code_change(term(), state(), term()) -> + {'ok', state()} | {'error', term()}. + code_change(_, State, _) -> case (State#state.module):init(State#state.args) of {ok, {SupFlags, StartSpec}} -> -- cgit v1.2.1 From 6a092c9622facf5076d191f35ee856f1c48d3ce4 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 26 Oct 2012 11:10:29 +0100 Subject: (un)cosmetic - reduce the diff --- src/supervisor2.erl | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 99c41aa6..2252c4b5 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -102,7 +102,7 @@ -type shutdown() :: 'brutal_kill' | timeout(). -type worker() :: 'worker' | 'supervisor'. -type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. --type sup_ref() :: (Name :: atom()) +-type sup_ref() :: (Name :: atom()) | {Name :: atom(), Node :: node()} | {'global', Name :: atom()} | pid(). @@ -119,8 +119,9 @@ %%-------------------------------------------------------------------------- --record(child, {pid = undefined, % pid is undefined when child is not running - name, +-record(child, {% pid is undefined when child is not running + pid = undefined, + name, mfa, restart_type, shutdown, @@ -592,7 +593,7 @@ update_childspec1([], Children, KeepOld) -> lists:reverse(Children ++ KeepOld). update_chsp(OldCh, Children) -> - case lists:map(fun (Ch) when OldCh#child.name =:= Ch#child.name -> + case lists:map(fun(Ch) when OldCh#child.name =:= Ch#child.name -> Ch#child{pid = OldCh#child.pid}; (Ch) -> Ch @@ -603,7 +604,7 @@ update_chsp(OldCh, Children) -> NewC -> {ok, NewC} end. - + %%% --------------------------------------------------- %%% Start a new child. %%% --------------------------------------------------- @@ -893,7 +894,7 @@ shutdown(Pid, brutal_kill) -> {'DOWN', _MRef, process, Pid, OtherReason} -> {error, OtherReason} end; - {error, Reason} -> + {error, Reason} -> {error, Reason} end; @@ -902,7 +903,7 @@ shutdown(Pid, Time) -> case monitor_child(Pid) of ok -> exit(Pid, shutdown), %% Try to shutdown gracefully - receive + receive {'DOWN', _MRef, process, Pid, shutdown} -> ok; {'DOWN', _MRef, process, Pid, OtherReason} -> @@ -914,14 +915,14 @@ shutdown(Pid, Time) -> {error, OtherReason} end end; - {error, Reason} -> + {error, Reason} -> {error, Reason} end. %% Help function to shutdown/2 switches from link to monitor approach monitor_child(Pid) -> - - %% Do the monitor operation first so that if the child dies + + %% Do the monitor operation first so that if the child dies %% before the monitoring is done causing a 'DOWN'-message with %% reason noproc, we will get the real reason in the 'EXIT'-message %% unless a naughty child has already done unlink... @@ -931,19 +932,19 @@ monitor_child(Pid) -> receive %% If the child dies before the unlik we must empty %% the mail-box of the 'EXIT'-message and the 'DOWN'-message. - {'EXIT', Pid, Reason} -> - receive + {'EXIT', Pid, Reason} -> + receive {'DOWN', _, process, Pid, _} -> {error, Reason} end - after 0 -> + after 0 -> %% If a naughty child did unlink and the child dies before %% monitor the result will be that shutdown/2 receives a %% 'DOWN'-message with reason noproc. %% If the child should die after the unlink there %% will be a 'DOWN'-message with a correct reason - %% that will be handled in shutdown/2. - ok + %% that will be handled in shutdown/2. + ok end. @@ -1021,11 +1022,11 @@ init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) -> validIntensity(MaxIntensity), validPeriod(Period), {ok, #state{name = supname(SupName,Mod), - strategy = Strategy, - intensity = MaxIntensity, - period = Period, - module = Mod, - args = Args}}; + strategy = Strategy, + intensity = MaxIntensity, + period = Period, + module = Mod, + args = Args}}; init_state1(_SupName, Type, _, _) -> {invalid_type, Type}. @@ -1038,14 +1039,14 @@ validStrategy(What) -> throw({invalid_strategy, What}). validIntensity(Max) when is_integer(Max), Max >= 0 -> true; -validIntensity(What) -> throw({invalid_intensity, What}). +validIntensity(What) -> throw({invalid_intensity, What}). validPeriod(Period) when is_integer(Period), Period > 0 -> true; validPeriod(What) -> throw({invalid_period, What}). -supname(self,Mod) -> {self(),Mod}; -supname(N,_) -> N. +supname(self, Mod) -> {self(), Mod}; +supname(N, _) -> N. %%% ------------------------------------------------------ %%% Check that the children start specification is valid. @@ -1124,7 +1125,7 @@ validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}). validMods(dynamic) -> true; validMods(Mods) when is_list(Mods) -> - lists:foreach(fun (Mod) -> + lists:foreach(fun(Mod) -> if is_atom(Mod) -> ok; true -> throw({invalid_module, Mod}) @@ -1142,7 +1143,7 @@ validMods(Mods) -> throw({invalid_modules, Mods}). %%% Returns: {ok, State'} | {terminate, State'} %%% ------------------------------------------------------ -add_restart(State) -> +add_restart(State) -> I = State#state.intensity, P = State#state.period, R = State#state.restarts, -- cgit v1.2.1 From 8282fc8c337d9c08bd044af9055dd42f185d2651 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 26 Oct 2012 14:23:33 +0100 Subject: Work in progress. Lots of (reverse) costmetic changes, pull over count_children/1 and move things around a bit. The OTP style of dynamic child handling hasn't been merged yet, so we're not even compiling yet. --- src/supervisor2.erl | 294 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 181 insertions(+), 113 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 2252c4b5..eae2f298 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -55,7 +55,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -75,15 +75,15 @@ -behaviour(gen_server). %% External exports --export([start_link/2,start_link/3, +-export([start_link/2, start_link/3, start_child/2, restart_child/2, delete_child/2, terminate_child/2, - which_children/1, find_child/2, - check_childspecs/1]). + which_children/1, count_children/1, + find_child/2, check_childspecs/1]). %% Internal exports --export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). --export([handle_cast/2]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). %%-------------------------------------------------------------------------- @@ -96,9 +96,7 @@ -type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. -type modules() :: [module()] | 'dynamic'. -type delay() :: non_neg_integer(). --type restart() :: 'permanent' | 'transient' | 'temporary' | 'intrinsic' - | {'permanent', delay()} | {'transient', delay()} - | {'intrinsic', delay()}. +-type restart() :: 'permanent' | 'transient' | 'temporary' | 'intrinsic' | {'permanent', delay()} | {'transient', delay()} | {'intrinsic', delay()}. -type shutdown() :: 'brutal_kill' | timeout(). -type worker() :: 'worker' | 'supervisor'. -type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. @@ -114,69 +112,69 @@ Modules :: modules()}. -type strategy() :: 'one_for_all' | 'one_for_one' - | 'rest_for_one' | 'simple_one_for_one' - | 'simple_one_for_one_terminate'. + | 'rest_for_one' | 'simple_one_for_one' | 'simple_one_for_one_terminate'. %%-------------------------------------------------------------------------- -record(child, {% pid is undefined when child is not running - pid = undefined, - name, - mfa, - restart_type, - shutdown, - child_type, - modules = []}). --type child_rec() :: #child{pid :: child() | {restarting,pid()} | [pid()], - name :: child_id(), - mfa :: mfargs(), - restart_type :: restart(), - shutdown :: shutdown(), - child_type :: worker(), - modules :: modules()}. + pid = undefined :: child() | {restarting,pid()} | [pid()], + name :: child_id(), + mfa :: mfargs(), + restart_type :: restart(), + shutdown :: shutdown(), + child_type :: worker(), + modules = [] :: modules()}). +-type child_rec() :: #child{}. -define(DICT, dict). +-define(SETS, sets). +-define(SET, set). -record(state, {name, - strategy, - children = [], - dynamics = ?DICT:new(), - intensity, - period, + strategy :: strategy(), + children = [] :: [child_rec()], + dynamics = ?DICT:new() :: ?DICT(), + intensity :: non_neg_integer(), + period :: pos_integer(), restarts = [], module, args}). --type state() :: #state{strategy :: strategy(), - children :: [child_rec()], - dynamics :: ?DICT(), - intensity :: non_neg_integer(), - period :: pos_integer()}. +-type state() :: #state{}. --define(is_simple(State), State#state.strategy =:= simple_one_for_one orelse - State#state.strategy =:= simple_one_for_one_terminate). - --define(is_terminate_simple(State), - State#state.strategy =:= simple_one_for_one_terminate). +-define(is_simple(State), State#state.strategy =:= simple_one_for_one orelse State#state.strategy =:= simple_one_for_one_terminate). +-define(is_terminate_simple(State), State#state.strategy =:= simple_one_for_one_terminate). -callback init(Args :: term()) -> {ok, {{RestartStrategy :: strategy(), - MaxR :: non_neg_integer(), - MaxT :: non_neg_integer()}, + MaxR :: non_neg_integer(), + MaxT :: non_neg_integer()}, [ChildSpec :: child_spec()]}} | ignore. +-define(restarting(_Pid_), {restarting,_Pid_}). + %%% --------------------------------------------------- %%% This is a general process supervisor built upon gen_server.erl. %%% Servers/processes should/could also be built using gen_server.erl. %%% SupName = {local, atom()} | {global, atom()}. %%% --------------------------------------------------- -start_link(Mod, Args) -> - gen_server:start_link(?MODULE, {self, Mod, Args}, []). +-type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). +-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. +-spec start_link(Module, Args) -> startlink_ret() when + Module :: module(), + Args :: term(). +start_link(Mod, Args) -> + gen_server:start_link(supervisor, {self, Mod, Args}, []). + +-spec start_link(SupName, Module, Args) -> startlink_ret() when + SupName :: sup_name(), + Module :: module(), + Args :: term(). start_link(SupName, Mod, Args) -> gen_server:start_link(SupName, ?MODULE, {SupName, Mod, Args}, []). - + %%% --------------------------------------------------- %%% Interface functions. %%% --------------------------------------------------- @@ -220,7 +218,6 @@ delete_child(Supervisor, Name) -> -spec terminate_child(SupRef, Id) -> Result when SupRef :: sup_ref(), - Id :: pid() | child_id(), Result :: 'ok' | {'error', Error}, Error :: 'not_found' | 'simple_one_for_one'. @@ -229,20 +226,29 @@ terminate_child(Supervisor, Name) -> -spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when SupRef :: sup_ref(), - Id :: child_id() | 'undefined', + Id :: child_id() | undefined, Child :: child(), Type :: worker(), Modules :: modules(). which_children(Supervisor) -> call(Supervisor, which_children). +-spec count_children(SupRef) -> PropListOfCounts when + SupRef :: sup_ref(), + PropListOfCounts :: [Count], + Count :: {specs, ChildSpecCount :: non_neg_integer()} + | {active, ActiveProcessCount :: non_neg_integer()} + | {supervisors, ChildSupervisorCount :: non_neg_integer()} + |{workers, ChildWorkerCount :: non_neg_integer()}. +count_children(Supervisor) -> + call(Supervisor, count_children). + call(Supervisor, Req) -> gen_server:call(Supervisor, Req, infinity). -spec check_childspecs(ChildSpecs) -> Result when ChildSpecs :: [child_spec()], Result :: 'ok' | {'error', Error :: term()}. - check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> case check_startspec(ChildSpecs) of {ok, _} -> ok; @@ -250,21 +256,22 @@ check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> end; check_childspecs(X) -> {error, {badarg, X}}. +%%%----------------------------------------------------------------- +%%% Called by timer:apply_after from restart/2 +%-spec try_again_restart(SupRef, Child) -> ok when +% SupRef :: sup_ref(), +% Child :: child_id() | pid(). +%try_again_restart(Supervisor, Child) -> +% cast(Supervisor, {try_again_restart, Child}). + find_child(Supervisor, Name) -> [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), Name1 =:= Name]. -%-export([behaviour_info/1]). - -%behaviour_info(callbacks) -> -% [{init,1}]; -%behaviour_info(_Other) -> -% undefined. - %%% --------------------------------------------------- -%%% +%%% %%% Initialize the supervisor. -%%% +%%% %%% --------------------------------------------------- -type init_sup_name() :: sup_name() | 'self'. @@ -321,12 +328,12 @@ init_dynamic(_State, StartSpec) -> %%----------------------------------------------------------------- %% Func: start_children/2 -%% Args: Children = [#child] in start order -%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} -%% Purpose: Start all children. The new list contains #child's +%% Args: Children = [child_rec()] in start order +%% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} +%% Purpose: Start all children. The new list contains #child's %% with pids. %% Returns: {ok, NChildren} | {error, NChildren} -%% NChildren = [#child] in termination order (reversed +%% NChildren = [child_rec()] in termination order (reversed %% start order) %%----------------------------------------------------------------- start_children(Children, SupName) -> start_children(Children, [], SupName). @@ -375,36 +382,47 @@ do_start_child_i(M, F, A) -> {error, What} end. - %%% --------------------------------------------------- -%%% +%%% %%% Callback functions. -%%% +%%% %%% --------------------------------------------------- --type call() :: 'which_children'. +-type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine -spec handle_call(call(), term(), state()) -> {'reply', term(), state()}. handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> - #child{mfa = {M, F, A}} = hd(State#state.children), + Child = hd(State#state.children), + #child{mfa = {M, F, A}} = Child, Args = A ++ EArgs, case do_start_child_i(M, F, Args) of {ok, undefined} -> {reply, {ok, undefined}, State}; {ok, Pid} -> - NState = State#state{dynamics = - ?DICT:store(Pid, Args, State#state.dynamics)}, + NState = State#state{dynamics = ?DICT:store(Pid, Args, State#state.dynamics)}, {reply, {ok, Pid}, NState}; {ok, Pid, Extra} -> - NState = State#state{dynamics = - ?DICT:store(Pid, Args, State#state.dynamics)}, + NState = State#state{dynamics = ?DICT:store(Pid, Args, State#state.dynamics)}, {reply, {ok, Pid, Extra}, NState}; What -> {reply, What, State} end; -%%% The requests terminate_child, delete_child and restart_child are -%%% invalid for simple_one_for_one and simple_one_for_one_terminate -%%% supervisors. +%% terminate_child for simple_one_for_one can only be done with pid +handle_call({terminate_child, Name}, _From, State) when not is_pid(Name), + ?is_simple(State) -> + {reply, {error, simple_one_for_one}, State}; + +handle_call({terminate_child, Name}, _From, State) -> + case get_child(Name, State) of + {value, Child} -> + NChild = do_terminate(Child, State#state.name), + {reply, ok, replace_child(NChild, State)}; + _ -> + {reply, {error, not_found}, State} + end; + +%%% The requests delete_child and restart_child are invalid for +%%% simple_one_for_one and simple_one_for_one_terminate supervisors. handle_call({_Req, _Data}, _From, State) when ?is_simple(State) -> {reply, {error, State#state.strategy}, State}; @@ -447,15 +465,6 @@ handle_call({delete_child, Name}, _From, State) -> {reply, {error, not_found}, State} end; -handle_call({terminate_child, Name}, _From, State) -> - case get_child(Name, State) of - {value, Child} -> - NChild = do_terminate(Child, State#state.name), - {reply, ok, replace_child(NChild, State)}; - _ -> - {reply, {error, not_found}, State} - end; - handle_call(which_children, _From, State) when ?is_simple(State) -> [#child{child_type = CT, modules = Mods}] = State#state.children, Reply = lists:map(fun ({Pid, _}) -> {undefined, Pid, CT, Mods} end, @@ -469,7 +478,58 @@ handle_call(which_children, _From, State) -> {Name, Pid, ChildType, Mods} end, State#state.children), - {reply, Resp, State}. + {reply, Resp, State}; + +handle_call(count_children, _From, #state{children = [#child{restart_type = temporary, + child_type = CT}]} = State) + when ?is_simple(State) -> + {Active, Count} = + ?SETS:fold(fun(Pid, {Alive, Tot}) -> + case is_pid(Pid) andalso is_process_alive(Pid) of + true ->{Alive+1, Tot +1}; + false -> + {Alive, Tot + 1} + end + end, {0, 0}, dynamics_db(temporary, State#state.dynamics)), + Reply = case CT of + supervisor -> [{specs, 1}, {active, Active}, + {supervisors, Count}, {workers, 0}]; + worker -> [{specs, 1}, {active, Active}, + {supervisors, 0}, {workers, Count}] + end, + {reply, Reply, State}; + +handle_call(count_children, _From, #state{children = [#child{restart_type = RType, + child_type = CT}]} = State) + when ?is_simple(State) -> + {Active, Count} = + ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) -> + case is_pid(Pid) andalso is_process_alive(Pid) of + true -> + {Alive+1, Tot +1}; + false -> + {Alive, Tot + 1} + end + end, {0, 0}, dynamics_db(RType, State#state.dynamics)), + Reply = case CT of + supervisor -> [{specs, 1}, {active, Active}, + {supervisors, Count}, {workers, 0}]; + worker -> [{specs, 1}, {active, Active}, + {supervisors, 0}, {workers, Count}] + end, + {reply, Reply, State}; + +handle_call(count_children, _From, State) -> + %% Specs and children are together on the children list... + {Specs, Active, Supers, Workers} = + lists:foldl(fun(Child, Counts) -> + count_child(Child, Counts) + end, {0,0,0,0}, State#state.children), + + %% Reformat counts to a property list. + Reply = [{specs, Specs}, {active, Active}, + {supervisors, Supers}, {workers, Workers}], + {reply, Reply, State}. -spec handle_cast('null', state()) -> {'noreply', state()}. %%% Hopefully cause a function-clause as there is no API function @@ -477,12 +537,35 @@ handle_call(which_children, _From, State) -> handle_cast(null, State) -> error_logger:error_msg("ERROR: Supervisor received cast-message 'null'~n", []), - {noreply, State}. +count_child(#child{pid = Pid, child_type = worker}, + {Specs, Active, Supers, Workers}) -> + case is_pid(Pid) andalso is_process_alive(Pid) of + true -> {Specs+1, Active+1, Supers, Workers+1}; + false -> {Specs+1, Active, Supers, Workers+1} + end; +count_child(#child{pid = Pid, child_type = supervisor}, + {Specs, Active, Supers, Workers}) -> + case is_pid(Pid) andalso is_process_alive(Pid) of + true -> {Specs+1, Active+1, Supers+1, Workers}; + false -> {Specs+1, Active, Supers+1, Workers} + end. + +%% +%% Take care of terminated children. +%% -spec handle_info(term(), state()) -> {'noreply', state()} | {'stop', 'shutdown', state()}. +handle_info({'EXIT', Pid, Reason}, State) -> + case restart_child(Pid, Reason, State) of + {ok, State1} -> + {noreply, State1}; + {shutdown, State1} -> + {stop, shutdown, State1} + end; + handle_info({delayed_restart, {RestartType, Reason, Child}}, State) when ?is_simple(State) -> {ok, NState} = do_restart(RestartType, Reason, Child, State), @@ -496,21 +579,11 @@ handle_info({delayed_restart, {RestartType, Reason, Child}}, State) -> {noreply, State} end; -%% -%% Take care of terminated children. -%% -handle_info({'EXIT', Pid, Reason}, State) -> - case restart_child(Pid, Reason, State) of - {ok, State1} -> - {noreply, State1}; - {shutdown, State1} -> - {stop, shutdown, State1} - end; - handle_info(Msg, State) -> error_logger:error_msg("Supervisor received unexpected message: ~p~n", [Msg]), {noreply, State}. + %% %% Terminate this server. %% @@ -563,14 +636,13 @@ check_flags({Strategy, MaxIntensity, Period}) -> check_flags(What) -> {bad_flags, What}. -update_childspec(State, StartSpec) when ?is_simple(State) -> +update_childspec(State, StartSpec) when ?is_simple(State) -> case check_startspec(StartSpec) of {ok, [Child]} -> {ok, State#state{children = [Child]}}; Error -> {error, Error} end; - update_childspec(State, StartSpec) -> case check_startspec(StartSpec) of {ok, Children} -> @@ -589,7 +661,7 @@ update_childspec1([Child|OldC], Children, KeepOld) -> update_childspec1(OldC, Children, [Child|KeepOld]) end; update_childspec1([], Children, KeepOld) -> - % Return them in (keeped) reverse start order. + %% Return them in (kept) reverse start order. lists:reverse(Children ++ KeepOld). update_chsp(OldCh, Children) -> @@ -615,14 +687,10 @@ handle_start_child(Child, State) -> case do_start_child(State#state.name, Child) of {ok, Pid} -> Children = State#state.children, - {{ok, Pid}, - State#state{children = - [Child#child{pid = Pid}|Children]}}; + {{ok, Pid}, State#state{children = [Child#child{pid = Pid}|Children]}}; {ok, Pid, Extra} -> Children = State#state.children, - {{ok, Pid, Extra}, - State#state{children = - [Child#child{pid = Pid}|Children]}}; + {{ok, Pid, Extra}, State#state{children = [Child#child{pid = Pid}|Children]}}; {error, What} -> {{error, {What, Child}}, State} end; @@ -634,7 +702,7 @@ handle_start_child(Child, State) -> %%% --------------------------------------------------- %%% Restart. A process has terminated. -%%% Returns: {ok, #state} | {shutdown, #state} +%%% Returns: {ok, state()} | {shutdown, state()} %%% --------------------------------------------------- restart_child(Pid, Reason, State) when ?is_simple(State) -> @@ -1002,12 +1070,12 @@ remove_child(Child, State) -> %% Type = {Strategy, MaxIntensity, Period} %% Strategy = one_for_one | one_for_all | simple_one_for_one | %% rest_for_one -%% MaxIntensity = integer() -%% Period = integer() +%% MaxIntensity = integer() >= 0 +%% Period = integer() > 0 %% Mod :== atom() -%% Arsg :== term() +%% Args :== term() %% Purpose: Check that Type is of correct type (!) -%% Returns: {ok, #state} | Error +%% Returns: {ok, state()} | Error %%----------------------------------------------------------------- init_state(SupName, Type, Mod, Args) -> case catch init_state1(SupName, Type, Mod, Args) of @@ -1053,15 +1121,15 @@ supname(N, _) -> N. %%% Shall be a six (6) tuple %%% {Name, Func, RestartType, Shutdown, ChildType, Modules} %%% where Name is an atom -%%% Func is {Mod, Fun, Args} == {atom, atom, list} +%%% Func is {Mod, Fun, Args} == {atom(), atom(), list()} %%% RestartType is permanent | temporary | transient | %%% intrinsic | {permanent, Delay} | %%% {transient, Delay} | {intrinsic, Delay} %% where Delay >= 0 -%%% Shutdown = integer() | infinity | brutal_kill +%%% Shutdown = integer() > 0 | infinity | brutal_kill %%% ChildType = supervisor | worker %%% Modules = [atom()] | dynamic -%%% Returns: {ok, [#child]} | Error +%%% Returns: {ok, [child_rec()]} | Error %%% ------------------------------------------------------ check_startspec(Children) -> check_startspec(Children, []). @@ -1117,7 +1185,7 @@ validDelay(Delay) when is_number(Delay), Delay >= 0 -> true; validDelay(What) -> throw({invalid_delay, What}). -validShutdown(Shutdown, _) +validShutdown(Shutdown, _) when is_integer(Shutdown), Shutdown > 0 -> true; validShutdown(infinity, supervisor) -> true; validShutdown(brutal_kill, _) -> true; -- cgit v1.2.1 From d8a163f5185bcb3849143f2af8e479d848bc0b93 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 29 Oct 2012 15:14:10 +0000 Subject: no more simple_one_for_one_terminate; include support for dynamic_db --- src/mirrored_supervisor.erl | 5 +- src/rabbit_amqqueue_sup.erl | 2 +- src/rabbit_channel_sup_sup.erl | 2 +- src/rabbit_client_sup.erl | 2 +- src/rabbit_mirror_queue_slave_sup.erl | 2 +- src/supervisor2.erl | 246 +++++++++++++++++++++++++++------- src/supervisor2_tests.erl | 2 +- src/test_sup.erl | 2 +- 8 files changed, 204 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/mirrored_supervisor.erl b/src/mirrored_supervisor.erl index 24c3ebd0..e070d520 100644 --- a/src/mirrored_supervisor.erl +++ b/src/mirrored_supervisor.erl @@ -212,9 +212,8 @@ start_link0(Prefix, Group, Init) -> init(Mod, Args) -> case Mod:init(Args) of {ok, {{Bad, _, _}, _ChildSpecs}} when - Bad =:= simple_one_for_one orelse - Bad =:= simple_one_for_one_terminate -> erlang:error(badarg); - Init -> Init + Bad =:= simple_one_for_one -> erlang:error(badarg); + Init -> Init end. start_child(Sup, ChildSpec) -> call(Sup, {start_child, ChildSpec}). diff --git a/src/rabbit_amqqueue_sup.erl b/src/rabbit_amqqueue_sup.erl index a4305e5f..7586fe46 100644 --- a/src/rabbit_amqqueue_sup.erl +++ b/src/rabbit_amqqueue_sup.erl @@ -47,6 +47,6 @@ start_child(Node, Args) -> supervisor2:start_child({?SERVER, Node}, Args). init([]) -> - {ok, {{simple_one_for_one_terminate, 10, 10}, + {ok, {{simple_one_for_one, 10, 10}, [{rabbit_amqqueue, {rabbit_amqqueue_process, start_link, []}, temporary, ?MAX_WAIT, worker, [rabbit_amqqueue_process]}]}}. diff --git a/src/rabbit_channel_sup_sup.erl b/src/rabbit_channel_sup_sup.erl index 995c41fb..29cd8787 100644 --- a/src/rabbit_channel_sup_sup.erl +++ b/src/rabbit_channel_sup_sup.erl @@ -43,6 +43,6 @@ start_channel(Pid, Args) -> %%---------------------------------------------------------------------------- init([]) -> - {ok, {{simple_one_for_one_terminate, 0, 1}, + {ok, {{simple_one_for_one, 0, 1}, [{channel_sup, {rabbit_channel_sup, start_link, []}, temporary, infinity, supervisor, [rabbit_channel_sup]}]}}. diff --git a/src/rabbit_client_sup.erl b/src/rabbit_client_sup.erl index c508f1b9..b4db97b1 100644 --- a/src/rabbit_client_sup.erl +++ b/src/rabbit_client_sup.erl @@ -44,5 +44,5 @@ start_link(SupName, Callback) -> supervisor2:start_link(SupName, ?MODULE, Callback). init({M,F,A}) -> - {ok, {{simple_one_for_one_terminate, 0, 1}, + {ok, {{simple_one_for_one, 0, 1}, [{client, {M,F,A}, temporary, infinity, supervisor, [M]}]}}. diff --git a/src/rabbit_mirror_queue_slave_sup.erl b/src/rabbit_mirror_queue_slave_sup.erl index a2034876..a20c50ef 100644 --- a/src/rabbit_mirror_queue_slave_sup.erl +++ b/src/rabbit_mirror_queue_slave_sup.erl @@ -31,7 +31,7 @@ start_link() -> supervisor2:start_link({local, ?SERVER}, ?MODULE, []). start_child(Node, Args) -> supervisor2:start_child({?SERVER, Node}, Args). init([]) -> - {ok, {{simple_one_for_one_terminate, 10, 10}, + {ok, {{simple_one_for_one, 10, 10}, [{rabbit_mirror_queue_slave, {rabbit_mirror_queue_slave, start_link, []}, temporary, ?MAX_WAIT, worker, [rabbit_mirror_queue_slave]}]}}. diff --git a/src/supervisor2.erl b/src/supervisor2.erl index eae2f298..2256b98e 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -3,12 +3,7 @@ %% %% 1) the module name is supervisor2 %% -%% 2) there is a new strategy called -%% simple_one_for_one_terminate. This is exactly the same as for -%% simple_one_for_one, except that children *are* explicitly -%% terminated as per the shutdown component of the child_spec. -%% -%% 3) child specifications can contain, as the restart type, a tuple +%% 2) child specifications can contain, as the restart type, a tuple %% {permanent, Delay} | {transient, Delay} | {intrinsic, Delay} %% where Delay >= 0 (see point (4) below for intrinsic). The delay, %% in seconds, indicates what should happen if a child, upon being @@ -41,14 +36,14 @@ %% perspective it's a normal exit, whilst from supervisor's %% perspective, it's an abnormal exit. %% -%% 4) Added an 'intrinsic' restart type. Like the transient type, this +%% 3) Added an 'intrinsic' restart type. Like the transient type, this %% type means the child should only be restarted if the child exits %% abnormally. Unlike the transient type, if the child exits %% normally, the supervisor itself also exits normally. If the %% child is a supervisor and it exits normally (i.e. with reason of %% 'shutdown') then the child's parent also exits normally. %% -%% 5) normal, and {shutdown, _} exit reasons are all treated the same +%% 4) normal, and {shutdown, _} exit reasons are all treated the same %% (i.e. are regarded as normal exits) %% %% All modifications are (C) 2010-2012 VMware, Inc. @@ -91,7 +86,7 @@ %%-------------------------------------------------------------------------- --type child() :: 'undefined' | pid(). +-type child() :: 'undefined' | pid(). -type child_id() :: term(). -type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. -type modules() :: [module()] | 'dynamic'. @@ -112,7 +107,7 @@ Modules :: modules()}. -type strategy() :: 'one_for_all' | 'one_for_one' - | 'rest_for_one' | 'simple_one_for_one' | 'simple_one_for_one_terminate'. + | 'rest_for_one' | 'simple_one_for_one'. %%-------------------------------------------------------------------------- @@ -132,17 +127,16 @@ -record(state, {name, strategy :: strategy(), - children = [] :: [child_rec()], - dynamics = ?DICT:new() :: ?DICT(), - intensity :: non_neg_integer(), - period :: pos_integer(), + children = [] :: [child_rec()], + dynamics :: ?DICT() | ?SET(), + intensity :: non_neg_integer(), + period :: pos_integer(), restarts = [], module, args}). -type state() :: #state{}. --define(is_simple(State), State#state.strategy =:= simple_one_for_one orelse State#state.strategy =:= simple_one_for_one_terminate). --define(is_terminate_simple(State), State#state.strategy =:= simple_one_for_one_terminate). +-define(is_simple(State), State#state.strategy =:= simple_one_for_one). -callback init(Args :: term()) -> {ok, {{RestartStrategy :: strategy(), @@ -166,7 +160,7 @@ Module :: module(), Args :: term(). start_link(Mod, Args) -> - gen_server:start_link(supervisor, {self, Mod, Args}, []). + gen_server:start_link(supervisor2, {self, Mod, Args}, []). -spec start_link(SupName, Module, Args) -> startlink_ret() when SupName :: sup_name(), @@ -398,10 +392,10 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> {ok, undefined} -> {reply, {ok, undefined}, State}; {ok, Pid} -> - NState = State#state{dynamics = ?DICT:store(Pid, Args, State#state.dynamics)}, + NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), {reply, {ok, Pid}, NState}; {ok, Pid, Extra} -> - NState = State#state{dynamics = ?DICT:store(Pid, Args, State#state.dynamics)}, + NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), {reply, {ok, Pid, Extra}, NState}; What -> {reply, What, State} @@ -422,7 +416,7 @@ handle_call({terminate_child, Name}, _From, State) -> end; %%% The requests delete_child and restart_child are invalid for -%%% simple_one_for_one and simple_one_for_one_terminate supervisors. +%%% simple_one_for_one supervisors. handle_call({_Req, _Data}, _From, State) when ?is_simple(State) -> {reply, {error, State#state.strategy}, State}; @@ -465,10 +459,20 @@ handle_call({delete_child, Name}, _From, State) -> {reply, {error, not_found}, State} end; -handle_call(which_children, _From, State) when ?is_simple(State) -> - [#child{child_type = CT, modules = Mods}] = State#state.children, +handle_call(which_children, _From, #state{children = [#child{restart_type = temporary, + child_type = CT, + modules = Mods}]} = + State) when ?is_simple(State) -> + Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end, + ?SETS:to_list(dynamics_db(temporary, State#state.dynamics))), + {reply, Reply, State}; + +handle_call(which_children, _From, #state{children = [#child{restart_type = RType, + child_type = CT, + modules = Mods}]} = + State) when ?is_simple(State) -> Reply = lists:map(fun ({Pid, _}) -> {undefined, Pid, CT, Mods} end, - ?DICT:to_list(State#state.dynamics)), + ?DICT:to_list(dynamics_db(RType, State#state.dynamics))), {reply, Reply, State}; handle_call(which_children, _From, State) -> @@ -589,13 +593,12 @@ handle_info(Msg, State) -> %% -spec terminate(term(), state()) -> 'ok'. -terminate(_Reason, State) when ?is_terminate_simple(State) -> - terminate_simple_children( - hd(State#state.children), State#state.dynamics, State#state.name), - ok; +terminate(_Reason, #state{children=[Child]} = State) when ?is_simple(State) -> + terminate_dynamic_children(Child, dynamics_db(Child#child.restart_type, + State#state.dynamics), + State#state.name); terminate(_Reason, State) -> - terminate_children(State#state.children, State#state.name), - ok. + terminate_children(State#state.children, State#state.name). %% %% Change code for the supervisor. @@ -686,11 +689,9 @@ handle_start_child(Child, State) -> false -> case do_start_child(State#state.name, Child) of {ok, Pid} -> - Children = State#state.children, - {{ok, Pid}, State#state{children = [Child#child{pid = Pid}|Children]}}; + {{ok, Pid}, save_child(Child#child{pid = Pid}, State)}; {ok, Pid, Extra} -> - Children = State#state.children, - {{ok, Pid, Extra}, State#state{children = [Child#child{pid = Pid}|Children]}}; + {{ok, Pid, Extra}, save_child(Child#child{pid = Pid}, State)}; {error, What} -> {{error, {What, Child}}, State} end; @@ -705,16 +706,15 @@ handle_start_child(Child, State) -> %%% Returns: {ok, state()} | {shutdown, state()} %%% --------------------------------------------------- -restart_child(Pid, Reason, State) when ?is_simple(State) -> - case ?DICT:find(Pid, State#state.dynamics) of +restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(State) -> + RestartType = Child#child.restart_type, + case dynamic_child_args(Pid, dynamics_db(RestartType, State#state.dynamics)) of {ok, Args} -> - [Child] = State#state.children, - RestartType = Child#child.restart_type, {M, F, _} = Child#child.mfa, NChild = Child#child{pid = Pid, mfa = {M, F, Args}}, do_restart(RestartType, Reason, NChild, State); error -> - {ok, State} + {ok, State} end; restart_child(Pid, Reason, State) -> Children = State#state.children, @@ -793,9 +793,7 @@ restart1(Child, State) -> {terminate, State} end. -restart(Strategy, Child, State, Restart) - when Strategy =:= simple_one_for_one orelse - Strategy =:= simple_one_for_one_terminate -> +restart(simple_one_for_one, Child, State, Restart) -> #child{mfa = {M, F, A}} = Child, Dynamics = ?DICT:erase(Child#child.pid, State#state.dynamics), case do_start_child_i(M, F, A) of @@ -859,12 +857,7 @@ terminate_children([], _SupName, Res) -> Res. terminate_simple_children(Child, Dynamics, SupName) -> - Pids = dict:fold(fun (Pid, _Args, Pids) -> - erlang:monitor(process, Pid), - unlink(Pid), - exit(Pid, child_exit_reason(Child)), - [Pid | Pids] - end, [], Dynamics), + Pids = monitor_children(Child, Dynamics), TimeoutMsg = {timeout, make_ref()}, TRef = timeout_start(Child, TimeoutMsg), {Replies, Timedout} = @@ -898,6 +891,21 @@ terminate_simple_children(Child, Dynamics, SupName) -> end || {Pid, Reply} <- Replies], ok. +monitor_children(Child=#child{restart_type=temporary}, Dynamics) -> + ?SETS:fold(fun (Pid, _Args, Pids) -> + erlang:monitor(process, Pid), + unlink(Pid), + exit(Pid, child_exit_reason(Child)), + [Pid | Pids] + end, [], Dynamics); +monitor_children(Child, Dynamics) -> + dict:fold(fun (Pid, _Args, Pids) -> + erlang:monitor(process, Pid), + unlink(Pid), + exit(Pid, child_exit_reason(Child)), + [Pid | Pids] + end, [], Dynamics). + child_exit_reason(#child{shutdown = brutal_kill}) -> kill; child_exit_reason(#child{}) -> shutdown. @@ -1016,11 +1024,149 @@ monitor_child(Pid) -> end. +%%----------------------------------------------------------------- +%% Func: terminate_dynamic_children/3 +%% Args: Child = child_rec() +%% Dynamics = ?DICT() | ?SET() +%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} +%% Returns: ok +%% +%% +%% Shutdown all dynamic children. This happens when the supervisor is +%% stopped. Because the supervisor can have millions of dynamic children, we +%% can have an significative overhead here. +%%----------------------------------------------------------------- +terminate_dynamic_children(Child, Dynamics, SupName) -> + {Pids, EStack0} = monitor_dynamic_children(Child, Dynamics), + Sz = ?SETS:size(Pids), + EStack = case Child#child.shutdown of + brutal_kill -> + ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids), + wait_dynamic_children(Child, Pids, Sz, undefined, EStack0); + infinity -> + ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids), + wait_dynamic_children(Child, Pids, Sz, undefined, EStack0); + Time -> + ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids), + TRef = erlang:start_timer(Time, self(), kill), + wait_dynamic_children(Child, Pids, Sz, TRef, EStack0) + end, + %% Unroll stacked errors and report them + ?DICT:fold(fun(Reason, Ls, _) -> + report_error(shutdown_error, Reason, + Child#child{pid=Ls}, SupName) + end, ok, EStack). + + +monitor_dynamic_children(#child{restart_type=temporary}, Dynamics) -> + ?SETS:fold(fun(P, {Pids, EStack}) -> + case monitor_child(P) of + ok -> + {?SETS:add_element(P, Pids), EStack}; + {error, normal} -> + {Pids, EStack}; + {error, Reason} -> + {Pids, ?DICT:append(Reason, P, EStack)} + end + end, {?SETS:new(), ?DICT:new()}, Dynamics); +monitor_dynamic_children(#child{restart_type=RType}, Dynamics) -> + ?DICT:fold(fun(P, _, {Pids, EStack}) when is_pid(P) -> + case monitor_child(P) of + ok -> + {?SETS:add_element(P, Pids), EStack}; + {error, normal} when RType =/= permanent -> + {Pids, EStack}; + {error, Reason} -> + {Pids, ?DICT:append(Reason, P, EStack)} + end; + (?restarting(_), _, {Pids, EStack}) -> + {Pids, EStack} + end, {?SETS:new(), ?DICT:new()}, Dynamics). + + +wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) -> + EStack; +wait_dynamic_children(_Child, _Pids, 0, TRef, EStack) -> + %% If the timer has expired before its cancellation, we must empty the + %% mail-box of the 'timeout'-message. + erlang:cancel_timer(TRef), + receive + {timeout, TRef, kill} -> + EStack + after 0 -> + EStack + end; +wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz, + TRef, EStack) -> + receive + {'DOWN', _MRef, process, Pid, killed} -> + wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, + TRef, EStack); + + {'DOWN', _MRef, process, Pid, Reason} -> + wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, + TRef, ?DICT:append(Reason, Pid, EStack)) + end; +wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, + TRef, EStack) -> + receive + {'DOWN', _MRef, process, Pid, shutdown} -> + wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, + TRef, EStack); + + {'DOWN', _MRef, process, Pid, normal} when RType =/= permanent -> + wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, + TRef, EStack); + + {'DOWN', _MRef, process, Pid, Reason} -> + wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, + TRef, ?DICT:append(Reason, Pid, EStack)); + + {timeout, TRef, kill} -> + ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids), + wait_dynamic_children(Child, Pids, Sz-1, undefined, EStack) + end. + %%----------------------------------------------------------------- %% Child/State manipulating functions. %%----------------------------------------------------------------- -state_del_child(#child{pid = Pid}, State) when ?is_simple(State) -> - NDynamics = ?DICT:erase(Pid, State#state.dynamics), + +%% Note we do not want to save the parameter list for temporary processes as +%% they will not be restarted, and hence we do not need this information. +%% Especially for dynamic children to simple_one_for_one supervisors +%% it could become very costly as it is not uncommon to spawn +%% very many such processes. +save_child(#child{restart_type = temporary, + mfa = {M, F, _}} = Child, #state{children = Children} = State) -> + State#state{children = [Child#child{mfa = {M, F, undefined}} |Children]}; +save_child(Child, #state{children = Children} = State) -> + State#state{children = [Child |Children]}. + +save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) -> + State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))}; +save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) -> + State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}. + +dynamics_db(temporary, undefined) -> + ?SETS:new(); +dynamics_db(_, undefined) -> + ?DICT:new(); +dynamics_db(_,Dynamics) -> + Dynamics. + +dynamic_child_args(Pid, Dynamics) -> + case ?SETS:is_set(Dynamics) of + true -> + {ok, undefined}; + false -> + ?DICT:find(Pid, Dynamics) + end. + +state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) -> + NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)), + State#state{dynamics = NDynamics}; +state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) -> + NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)), State#state{dynamics = NDynamics}; state_del_child(Child, State) -> NChildren = del_child(Child#child.name, State#state.children), @@ -1051,6 +1197,7 @@ split_child(_, [], After) -> get_child(Name, State) -> lists:keysearch(Name, #child.name, State#state.children). + replace_child(Child, State) -> Chs = do_replace_child(Child, State#state.children), State#state{children = Chs}. @@ -1098,7 +1245,6 @@ init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) -> init_state1(_SupName, Type, _, _) -> {invalid_type, Type}. -validStrategy(simple_one_for_one_terminate) -> true; validStrategy(simple_one_for_one) -> true; validStrategy(one_for_one) -> true; validStrategy(one_for_all) -> true; diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index e42ded7b..ff1a7f3c 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -65,6 +65,6 @@ init([Timeout]) -> [{local, ?MODULE}, ?MODULE, []]}, transient, Timeout, supervisor, [?MODULE]}]}}; init([]) -> - {ok, {{simple_one_for_one_terminate, 0, 1}, + {ok, {{simple_one_for_one, 0, 1}, [{test_worker, {?MODULE, start_link, []}, temporary, 1000, worker, [?MODULE]}]}}. diff --git a/src/test_sup.erl b/src/test_sup.erl index 7f4b5049..6a56e64a 100644 --- a/src/test_sup.erl +++ b/src/test_sup.erl @@ -34,7 +34,7 @@ %%---------------------------------------------------------------------------- test_supervisor_delayed_restart() -> - passed = with_sup(simple_one_for_one_terminate, + passed = with_sup(simple_one_for_one, fun (SupPid) -> {ok, _ChildPid} = supervisor2:start_child(SupPid, []), -- cgit v1.2.1 From bbe8ed714286772fed5c7326b6e819284888de53 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 26 Nov 2012 13:21:26 +0000 Subject: managed restarts, try-again-restart, cosmetics --- src/supervisor2.erl | 378 ++++++++++++++++++++++++++++------------------------ 1 file changed, 205 insertions(+), 173 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 2256b98e..be6319d6 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -79,6 +79,7 @@ %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([try_again_restart/2]). %%-------------------------------------------------------------------------- @@ -112,13 +113,13 @@ %%-------------------------------------------------------------------------- -record(child, {% pid is undefined when child is not running - pid = undefined :: child() | {restarting,pid()} | [pid()], - name :: child_id(), - mfa :: mfargs(), + pid = undefined :: child() | {restarting,pid()} | [pid()], + name :: child_id(), + mfargs :: mfargs(), restart_type :: restart(), shutdown :: shutdown(), - child_type :: worker(), - modules = [] :: modules()}). + child_type :: worker(), + modules = [] :: modules()}). -type child_rec() :: #child{}. -define(DICT, dict). @@ -191,7 +192,8 @@ start_child(Supervisor, ChildSpec) -> Result :: {'ok', Child :: child()} | {'ok', Child :: child(), Info :: term()} | {'error', Error}, - Error :: 'running' | 'not_found' | 'simple_one_for_one' | term(). + Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one' | + term(). restart_child(Supervisor, Name) -> call(Supervisor, {restart_child, Name}). @@ -199,7 +201,7 @@ restart_child(Supervisor, Name) -> SupRef :: sup_ref(), Id :: child_id(), Result :: 'ok' | {'error', Error}, - Error :: 'running' | 'not_found' | 'simple_one_for_one'. + Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one'. delete_child(Supervisor, Name) -> call(Supervisor, {delete_child, Name}). @@ -221,7 +223,7 @@ terminate_child(Supervisor, Name) -> -spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when SupRef :: sup_ref(), Id :: child_id() | undefined, - Child :: child(), + Child :: child() | 'restarting', Type :: worker(), Modules :: modules(). which_children(Supervisor) -> @@ -252,11 +254,14 @@ check_childspecs(X) -> {error, {badarg, X}}. %%%----------------------------------------------------------------- %%% Called by timer:apply_after from restart/2 -%-spec try_again_restart(SupRef, Child) -> ok when -% SupRef :: sup_ref(), -% Child :: child_id() | pid(). -%try_again_restart(Supervisor, Child) -> -% cast(Supervisor, {try_again_restart, Child}). +-spec try_again_restart(SupRef, Child) -> ok when + SupRef :: sup_ref(), + Child :: child_id() | pid(). +try_again_restart(Supervisor, Child) -> + cast(Supervisor, {try_again_restart, Child}). + +cast(Supervisor, Req) -> + gen_server:cast(Supervisor, Req). find_child(Supervisor, Name) -> [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), @@ -334,6 +339,8 @@ start_children(Children, SupName) -> start_children(Children, [], SupName). start_children([Child|Chs], NChildren, SupName) -> case do_start_child(SupName, Child) of + {ok, undefined} when Child#child.restart_type =:= temporary -> + start_children(Chs, NChildren, SupName); {ok, Pid} -> start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); {ok, Pid, _Extra} -> @@ -346,8 +353,8 @@ start_children([], NChildren, _SupName) -> {ok, NChildren}. do_start_child(SupName, Child) -> - #child{mfa = {M, F, A}} = Child, - case catch apply(M, F, A) of + #child{mfargs = {M, F, Args}} = Child, + case catch apply(M, F, Args) of {ok, Pid} when is_pid(Pid) -> NChild = Child#child{pid = Pid}, report_progress(NChild, SupName), @@ -386,11 +393,11 @@ do_start_child_i(M, F, A) -> handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> Child = hd(State#state.children), - #child{mfa = {M, F, A}} = Child, + #child{mfargs = {M, F, A}} = Child, Args = A ++ EArgs, case do_start_child_i(M, F, Args) of - {ok, undefined} -> - {reply, {ok, undefined}, State}; + {ok, undefined} when Child#child.restart_type =:= temporary -> + {reply, {ok, undefined}, State}; {ok, Pid} -> NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), {reply, {ok, Pid}, NState}; @@ -407,11 +414,15 @@ handle_call({terminate_child, Name}, _From, State) when not is_pid(Name), {reply, {error, simple_one_for_one}, State}; handle_call({terminate_child, Name}, _From, State) -> - case get_child(Name, State) of + case get_child(Name, State, ?is_simple(State)) of {value, Child} -> - NChild = do_terminate(Child, State#state.name), - {reply, ok, replace_child(NChild, State)}; - _ -> + case do_terminate(Child, State#state.name) of + #child{restart_type=RT} when RT=:=temporary; ?is_simple(State) -> + {reply, ok, state_del_child(Child, State)}; + NChild -> + {reply, ok, replace_child(NChild, State)} + end; + false -> {reply, {error, not_found}, State} end; @@ -442,6 +453,8 @@ handle_call({restart_child, Name}, _From, State) -> Error -> {reply, Error, State} end; + {value, #child{pid=?restarting(_)}} -> + {reply, {error, restarting}, State}; {value, _} -> {reply, {error, running}, State}; _ -> @@ -453,6 +466,8 @@ handle_call({delete_child, Name}, _From, State) -> {value, Child} when Child#child.pid =:= undefined -> NState = remove_child(Child, State), {reply, ok, NState}; + {value, #child{pid=?restarting(_)}} -> + {reply, {error, restarting}, State}; {value, _} -> {reply, {error, running}, State}; _ -> @@ -471,19 +486,24 @@ handle_call(which_children, _From, #state{children = [#child{restart_type = RTyp child_type = CT, modules = Mods}]} = State) when ?is_simple(State) -> - Reply = lists:map(fun ({Pid, _}) -> {undefined, Pid, CT, Mods} end, + Reply = lists:map(fun({?restarting(_),_}) -> {undefined,restarting,CT,Mods}; + ({Pid, _}) -> {undefined, Pid, CT, Mods} end, ?DICT:to_list(dynamics_db(RType, State#state.dynamics))), {reply, Reply, State}; handle_call(which_children, _From, State) -> Resp = - lists:map(fun (#child{pid = Pid, name = Name, + lists:map(fun(#child{pid = ?restarting(_), name = Name, + child_type = ChildType, modules = Mods}) -> + {Name, restarting, ChildType, Mods}; + (#child{pid = Pid, name = Name, child_type = ChildType, modules = Mods}) -> - {Name, Pid, ChildType, Mods} + {Name, Pid, ChildType, Mods} end, State#state.children), {reply, Resp, State}; + handle_call(count_children, _From, #state{children = [#child{restart_type = temporary, child_type = CT}]} = State) when ?is_simple(State) -> @@ -535,13 +555,6 @@ handle_call(count_children, _From, State) -> {supervisors, Supers}, {workers, Workers}], {reply, Reply, State}. --spec handle_cast('null', state()) -> {'noreply', state()}. -%%% Hopefully cause a function-clause as there is no API function -%%% that utilizes cast. -handle_cast(null, State) -> - error_logger:error_msg("ERROR: Supervisor received cast-message 'null'~n", - []), - {noreply, State}. count_child(#child{pid = Pid, child_type = worker}, {Specs, Active, Supers, Workers}) -> @@ -556,6 +569,44 @@ count_child(#child{pid = Pid, child_type = supervisor}, false -> {Specs+1, Active, Supers+1, Workers} end. + +%%% If a restart attempt failed, this message is sent via +%%% timer:apply_after(0,...) in order to give gen_server the chance to +%%% check it's inbox before trying again. +-spec handle_cast({try_again_restart, child_id() | pid()}, state()) -> + {'noreply', state()} | {stop, shutdown, state()}. + +handle_cast({try_again_restart,Pid}, #state{children=[Child]}=State) + when ?is_simple(State) -> + RT = Child#child.restart_type, + RPid = restarting(Pid), + case dynamic_child_args(RPid, dynamics_db(RT, State#state.dynamics)) of + {ok, Args} -> + {M, F, _} = Child#child.mfargs, + NChild = Child#child{pid = RPid, mfargs = {M, F, Args}}, + case restart(NChild,State) of + {ok, State1} -> + {noreply, State1}; + {shutdown, State1} -> + {stop, shutdown, State1} + end; + error -> + {noreply, State} + end; + +handle_cast({try_again_restart,Name}, State) -> + case lists:keyfind(Name,#child.name,State#state.children) of + Child = #child{pid=?restarting(_)} -> + case restart(Child,State) of + {ok, State1} -> + {noreply, State1}; + {shutdown, State1} -> + {stop, shutdown, State1} + end; + _ -> + {noreply,State} + end. + %% %% Take care of terminated children. %% @@ -584,7 +635,7 @@ handle_info({delayed_restart, {RestartType, Reason, Child}}, State) -> end; handle_info(Msg, State) -> - error_logger:error_msg("Supervisor received unexpected message: ~p~n", + error_logger:error_msg("Supervisor received unexpected message: ~p~n", [Msg]), {noreply, State}. @@ -688,6 +739,8 @@ handle_start_child(Child, State) -> case get_child(Child#child.name, State) of false -> case do_start_child(State#state.name, Child) of + {ok, undefined} when Child#child.restart_type =:= temporary -> + {{ok, undefined}, State}; {ok, Pid} -> {{ok, Pid}, save_child(Child#child{pid = Pid}, State)}; {ok, Pid, Extra} -> @@ -695,7 +748,7 @@ handle_start_child(Child, State) -> {error, What} -> {{error, {What, Child}}, State} end; - {value, OldChild} when OldChild#child.pid =/= undefined -> + {value, OldChild} when is_pid(OldChild#child.pid) -> {{error, {already_started, OldChild#child.pid}}, State}; {value, _OldChild} -> {{error, already_present}, State} @@ -710,8 +763,8 @@ restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(S RestartType = Child#child.restart_type, case dynamic_child_args(Pid, dynamics_db(RestartType, State#state.dynamics)) of {ok, Args} -> - {M, F, _} = Child#child.mfa, - NChild = Child#child{pid = Pid, mfa = {M, F, Args}}, + {M, F, _} = Child#child.mfargs, + NChild = Child#child{pid = Pid, mfargs = {M, F, Args}}, do_restart(RestartType, Reason, NChild, State); error -> {ok, State} @@ -753,7 +806,7 @@ do_restart(temporary, Reason, Child, State) -> {ok, NState}. do_restart_delay({RestartType, Delay}, Reason, Child, State) -> - case restart1(Child, State) of + case restart(Child, State) of {ok, NState} -> {ok, NState}; {terminate, NState} -> @@ -773,32 +826,32 @@ del_child_and_maybe_shutdown(_, Child, State) -> restart(Child, State) -> case add_restart(State) of {ok, NState} -> - restart(NState#state.strategy, Child, NState, fun restart/2); + case restart(NState#state.strategy, Child, NState) of + {try_again,NState2} -> + %% Leaving control back to gen_server before + %% trying again. This way other incoming requsts + %% for the supervisor can be handled - e.g. a + %% shutdown request for the supervisor or the + %% child. + Id = if ?is_simple(State) -> Child#child.pid; + true -> Child#child.name + end, + timer:apply_after(0,?MODULE,try_again_restart,[self(),Id]), + {ok,NState2}; + Other -> + Other + end; {terminate, NState} -> report_error(shutdown, reached_max_restart_intensity, Child, State#state.name), - {shutdown, state_del_child(Child, NState)} + {shutdown, remove_child(Child, NState)} end. -restart1(Child, State) -> - case add_restart(State) of - {ok, NState} -> - restart(NState#state.strategy, Child, NState, fun restart1/2); - {terminate, _NState} -> - %% we've reached the max restart intensity, but the - %% add_restart will have added to the restarts - %% field. Given we don't want to die here, we need to go - %% back to the old restarts field otherwise we'll never - %% attempt to restart later. - {terminate, State} - end. - -restart(simple_one_for_one, Child, State, Restart) -> - #child{mfa = {M, F, A}} = Child, - Dynamics = ?DICT:erase(Child#child.pid, State#state.dynamics), +restart(simple_one_for_one, Child, State) -> + #child{pid = OldPid, mfargs = {M, F, A}} = Child, + Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type, + State#state.dynamics)), case do_start_child_i(M, F, A) of - {ok, undefined} -> - {ok, State}; {ok, Pid} -> NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, {ok, NState}; @@ -806,10 +859,13 @@ restart(simple_one_for_one, Child, State, Restart) -> NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, {ok, NState}; {error, Error} -> + NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A, + Dynamics)}, report_error(start_error, Error, Child, State#state.name), - Restart(Child, State) + {try_again, NState} end; -restart(one_for_one, Child, State, Restart) -> +restart(one_for_one, Child, State) -> + OldPid = Child#child.pid, case do_start_child(State#state.name, Child) of {ok, Pid} -> NState = replace_child(Child#child{pid = Pid}, State), @@ -818,149 +874,90 @@ restart(one_for_one, Child, State, Restart) -> NState = replace_child(Child#child{pid = Pid}, State), {ok, NState}; {error, Reason} -> + NState = replace_child(Child#child{pid = restarting(OldPid)}, State), report_error(start_error, Reason, Child, State#state.name), - Restart(Child, State) + {try_again, NState} end; -restart(rest_for_one, Child, State, Restart) -> +restart(rest_for_one, Child, State) -> {ChAfter, ChBefore} = split_child(Child#child.pid, State#state.children), ChAfter2 = terminate_children(ChAfter, State#state.name), case start_children(ChAfter2, State#state.name) of {ok, ChAfter3} -> {ok, State#state{children = ChAfter3 ++ ChBefore}}; {error, ChAfter3} -> - Restart(Child, State#state{children = ChAfter3 ++ ChBefore}) + NChild = Child#child{pid=restarting(Child#child.pid)}, + NState = State#state{children = ChAfter3 ++ ChBefore}, + {try_again, replace_child(NChild,NState)} end; -restart(one_for_all, Child, State, Restart) -> +restart(one_for_all, Child, State) -> Children1 = del_child(Child#child.pid, State#state.children), Children2 = terminate_children(Children1, State#state.name), case start_children(Children2, State#state.name) of {ok, NChs} -> {ok, State#state{children = NChs}}; {error, NChs} -> - Restart(Child, State#state{children = NChs}) + NChild = Child#child{pid=restarting(Child#child.pid)}, + NState = State#state{children = NChs}, + {try_again, replace_child(NChild,NState)} end. +restarting(Pid) when is_pid(Pid) -> ?restarting(Pid); +restarting(RPid) -> RPid. + %%----------------------------------------------------------------- %% Func: terminate_children/2 -%% Args: Children = [#child] in termination order +%% Args: Children = [child_rec()] in termination order %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} -%% Returns: NChildren = [#child] in +%% Returns: NChildren = [child_rec()] in %% startup order (reversed termination order) %%----------------------------------------------------------------- terminate_children(Children, SupName) -> terminate_children(Children, SupName, []). +%% Temporary children should not be restarted and thus should +%% be skipped when building the list of terminated children, although +%% we do want them to be shut down as many functions from this module +%% use this function to just clear everything. +terminate_children([Child = #child{restart_type=temporary} | Children], SupName, Res) -> + do_terminate(Child, SupName), + terminate_children(Children, SupName, Res); terminate_children([Child | Children], SupName, Res) -> NChild = do_terminate(Child, SupName), terminate_children(Children, SupName, [NChild | Res]); terminate_children([], _SupName, Res) -> Res. -terminate_simple_children(Child, Dynamics, SupName) -> - Pids = monitor_children(Child, Dynamics), - TimeoutMsg = {timeout, make_ref()}, - TRef = timeout_start(Child, TimeoutMsg), - {Replies, Timedout} = - lists:foldl( - fun (_Pid, {Replies, Timedout}) -> - {Pid1, Reason1, Timedout1} = - receive - TimeoutMsg -> - Remaining = Pids -- [P || {P, _} <- Replies], - [exit(P, kill) || P <- Remaining], - receive - {'DOWN', _MRef, process, Pid, Reason} -> - {Pid, Reason, true} - end; - {'DOWN', _MRef, process, Pid, Reason} -> - {Pid, Reason, Timedout} - end, - {[{Pid1, child_res(Child, Reason1, Timedout1)} | Replies], - Timedout1} - end, {[], false}, Pids), - timeout_stop(Child, TRef, TimeoutMsg, Timedout), - ReportError = shutdown_error_reporter(SupName), - Report = fun(_, ok) -> ok; - (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid}) - end, - [receive - {'EXIT', Pid, Reason} -> - Report(Pid, child_res(Child, Reason, Timedout)) - after - 0 -> Report(Pid, Reply) - end || {Pid, Reply} <- Replies], - ok. - -monitor_children(Child=#child{restart_type=temporary}, Dynamics) -> - ?SETS:fold(fun (Pid, _Args, Pids) -> - erlang:monitor(process, Pid), - unlink(Pid), - exit(Pid, child_exit_reason(Child)), - [Pid | Pids] - end, [], Dynamics); -monitor_children(Child, Dynamics) -> - dict:fold(fun (Pid, _Args, Pids) -> - erlang:monitor(process, Pid), - unlink(Pid), - exit(Pid, child_exit_reason(Child)), - [Pid | Pids] - end, [], Dynamics). - -child_exit_reason(#child{shutdown = brutal_kill}) -> kill; -child_exit_reason(#child{}) -> shutdown. - -child_res(#child{shutdown=brutal_kill}, killed, false) -> ok; -child_res(#child{}, shutdown, false) -> ok; -child_res(#child{restart_type=permanent}, normal, false) -> {error, normal}; -child_res(#child{restart_type={permanent,_}},normal, false) -> {error, normal}; -child_res(#child{}, normal, false) -> ok; -child_res(#child{}, R, _) -> {error, R}. - -timeout_start(#child{shutdown = Time}, Msg) when is_integer(Time) -> - erlang:send_after(Time, self(), Msg); -timeout_start(#child{}, _Msg) -> - ok. - -timeout_stop(#child{shutdown = Time}, TRef, Msg, false) when is_integer(Time) -> - erlang:cancel_timer(TRef), - receive - Msg -> ok - after - 0 -> ok - end; -timeout_stop(#child{}, _TRef, _Msg, _Timedout) -> - ok. - -do_terminate(Child, SupName) when Child#child.pid =/= undefined -> - ReportError = shutdown_error_reporter(SupName), +do_terminate(Child, SupName) when is_pid(Child#child.pid) -> case shutdown(Child#child.pid, Child#child.shutdown) of ok -> ok; {error, normal} -> case Child#child.restart_type of - permanent -> ReportError(normal, Child); - {permanent, _Delay} -> ReportError(normal, Child); - _ -> ok + permanent -> + report_error(shutdown_error, normal, Child, SupName); + {permanent, _Delay} -> + report_error(shutdown_error, normal, Child, SupName); + _ -> + ok end; {error, OtherReason} -> - ReportError(OtherReason, Child) + report_error(shutdown_error, OtherReason, Child, SupName) end, Child#child{pid = undefined}; do_terminate(Child, _SupName) -> - Child. + Child#child{pid = undefined}. %%----------------------------------------------------------------- -%% Shutdowns a child. We must check the EXIT value +%% Shutdowns a child. We must check the EXIT value %% of the child, because it might have died with another reason than -%% the wanted. In that case we want to report the error. We put a -%% monitor on the child an check for the 'DOWN' message instead of -%% checking for the 'EXIT' message, because if we check the 'EXIT' -%% message a "naughty" child, who does unlink(Sup), could hang the -%% supervisor. +%% the wanted. In that case we want to report the error. We put a +%% monitor on the child an check for the 'DOWN' message instead of +%% checking for the 'EXIT' message, because if we check the 'EXIT' +%% message a "naughty" child, who does unlink(Sup), could hang the +%% supervisor. %% Returns: ok | {error, OtherReason} (this should be reported) %%----------------------------------------------------------------- shutdown(Pid, brutal_kill) -> - case monitor_child(Pid) of ok -> exit(Pid, kill), @@ -973,9 +970,7 @@ shutdown(Pid, brutal_kill) -> {error, Reason} -> {error, Reason} end; - shutdown(Pid, Time) -> - case monitor_child(Pid) of ok -> exit(Pid, shutdown), %% Try to shutdown gracefully @@ -1015,7 +1010,7 @@ monitor_child(Pid) -> end after 0 -> %% If a naughty child did unlink and the child dies before - %% monitor the result will be that shutdown/2 receives a + %% monitor the result will be that shutdown/2 receives a %% 'DOWN'-message with reason noproc. %% If the child should die after the unlink there %% will be a 'DOWN'-message with a correct reason @@ -1137,8 +1132,8 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, %% it could become very costly as it is not uncommon to spawn %% very many such processes. save_child(#child{restart_type = temporary, - mfa = {M, F, _}} = Child, #state{children = Children} = State) -> - State#state{children = [Child#child{mfa = {M, F, undefined}} |Children]}; + mfargs = {M, F, _}} = Child, #state{children = Children} = State) -> + State#state{children = [Child#child{mfargs = {M, F, undefined}} |Children]}; save_child(Child, #state{children = Children} = State) -> State#state{children = [Child |Children]}. @@ -1172,8 +1167,12 @@ state_del_child(Child, State) -> NChildren = del_child(Child#child.name, State#state.children), State#state{children = NChildren}. +del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary -> + Chs; del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name -> [Ch#child{pid = undefined} | Chs]; +del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary -> + Chs; del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid -> [Ch#child{pid = undefined} | Chs]; del_child(Name, [Ch|Chs]) -> @@ -1196,8 +1195,38 @@ split_child(_, [], After) -> {lists:reverse(After), []}. get_child(Name, State) -> + get_child(Name, State, false). +get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) -> + get_dynamic_child(Pid, State); +get_child(Name, State, _) -> lists:keysearch(Name, #child.name, State#state.children). +get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) -> + DynamicsDb = dynamics_db(Child#child.restart_type, Dynamics), + case is_dynamic_pid(Pid, DynamicsDb) of + true -> + {value, Child#child{pid=Pid}}; + false -> + RPid = restarting(Pid), + case is_dynamic_pid(RPid, DynamicsDb) of + true -> + {value, Child#child{pid=RPid}}; + false -> + case erlang:is_process_alive(Pid) of + true -> false; + false -> {value, Child} + end + end + end. + +is_dynamic_pid(Pid, Dynamics) -> + case ?SETS:is_set(Dynamics) of + true -> + ?SETS:is_element(Pid, Dynamics); + false -> + ?DICT:is_key(Pid, Dynamics) + end. + replace_child(Child, State) -> Chs = do_replace_child(Child, State#state.children), State#state{children = Chs}. @@ -1245,11 +1274,11 @@ init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) -> init_state1(_SupName, Type, _, _) -> {invalid_type, Type}. -validStrategy(simple_one_for_one) -> true; -validStrategy(one_for_one) -> true; -validStrategy(one_for_all) -> true; -validStrategy(rest_for_one) -> true; -validStrategy(What) -> throw({invalid_strategy, What}). +validStrategy(simple_one_for_one) -> true; +validStrategy(one_for_one) -> true; +validStrategy(one_for_all) -> true; +validStrategy(rest_for_one) -> true; +validStrategy(What) -> throw({invalid_strategy, What}). validIntensity(Max) when is_integer(Max), Max >= 0 -> true; @@ -1303,7 +1332,7 @@ check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) -> validChildType(ChildType), validShutdown(Shutdown, ChildType), validMods(Mods), - {ok, #child{name = Name, mfa = Func, restart_type = RestartType, + {ok, #child{name = Name, mfargs = Func, restart_type = RestartType, shutdown = Shutdown, child_type = ChildType, modules = Mods}}. validChildType(supervisor) -> true; @@ -1312,8 +1341,8 @@ validChildType(What) -> throw({invalid_child_type, What}). validName(_Name) -> true. -validFunc({M, F, A}) when is_atom(M), - is_atom(F), +validFunc({M, F, A}) when is_atom(M), + is_atom(F), is_list(A) -> true; validFunc(Func) -> throw({invalid_mfa, Func}). @@ -1413,15 +1442,18 @@ report_error(Error, Reason, Child, SupName) -> {offender, extract_child(Child)}], error_logger:error_report(supervisor_report, ErrorMsg). -shutdown_error_reporter(SupName) -> - fun(Reason, Child) -> - report_error(shutdown_error, Reason, Child, SupName) - end. +extract_child(Child) when is_list(Child#child.pid) -> + [{nb_children, length(Child#child.pid)}, + {name, Child#child.name}, + {mfargs, Child#child.mfargs}, + {restart_type, Child#child.restart_type}, + {shutdown, Child#child.shutdown}, + {child_type, Child#child.child_type}]; extract_child(Child) -> [{pid, Child#child.pid}, {name, Child#child.name}, - {mfa, Child#child.mfa}, + {mfargs, Child#child.mfargs}, {restart_type, Child#child.restart_type}, {shutdown, Child#child.shutdown}, {child_type, Child#child.child_type}]. -- cgit v1.2.1 From 721de630fdd3e7817ece0e788475a0081fcbc749 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 27 Nov 2012 12:42:10 +0000 Subject: tweak for {Mode, Delay} restart types; tweak test timings a little --- src/supervisor2.erl | 29 +++++++++++++++++++++++++---- src/test_sup.erl | 4 ++-- 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index be6319d6..c5a16a9f 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -338,8 +338,12 @@ init_dynamic(_State, StartSpec) -> start_children(Children, SupName) -> start_children(Children, [], SupName). start_children([Child|Chs], NChildren, SupName) -> + Restart = case Child#child.restart_type of + A when is_atom(A) -> A; + {N, _} when is_atom(N) -> N + end, case do_start_child(SupName, Child) of - {ok, undefined} when Child#child.restart_type =:= temporary -> + {ok, undefined} when Restart =:= temporary -> start_children(Chs, NChildren, SupName); {ok, Pid} -> start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); @@ -394,9 +398,13 @@ do_start_child_i(M, F, A) -> handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> Child = hd(State#state.children), #child{mfargs = {M, F, A}} = Child, + Restart = case Child#child.restart_type of + Name when is_atom(Name) -> Name; + {Type, _} when is_atom(Type) -> Type + end, Args = A ++ EArgs, case do_start_child_i(M, F, Args) of - {ok, undefined} when Child#child.restart_type =:= temporary -> + {ok, undefined} when Restart =:= temporary -> {reply, {ok, undefined}, State}; {ok, Pid} -> NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), @@ -630,7 +638,7 @@ handle_info({delayed_restart, {RestartType, Reason, Child}}, State) -> {value, Child1} -> {ok, NState} = do_restart(RestartType, Reason, Child1, State), {noreply, NState}; - _ -> + What -> {noreply, State} end; @@ -806,7 +814,7 @@ do_restart(temporary, Reason, Child, State) -> {ok, NState}. do_restart_delay({RestartType, Delay}, Reason, Child, State) -> - case restart(Child, State) of + case restart1(Child, State) of {ok, NState} -> {ok, NState}; {terminate, NState} -> @@ -816,6 +824,19 @@ do_restart_delay({RestartType, Delay}, Reason, Child, State) -> {ok, state_del_child(Child, NState)} end. +restart1(Child, State) -> + case add_restart(State) of + {ok, NState} -> + restart(NState#state.strategy, Child, NState); + {terminate, _NState} -> + %% we've reached the max restart intensity, but the + %% add_restart will have added to the restarts + %% field. Given we don't want to die here, we need to go + %% back to the old restarts field otherwise we'll never + %% attempt to restart later. + {terminate, State} + end. + del_child_and_maybe_shutdown(intrinsic, Child, State) -> {shutdown, state_del_child(Child, State)}; del_child_and_maybe_shutdown({intrinsic, _Delay}, Child, State) -> diff --git a/src/test_sup.erl b/src/test_sup.erl index 6a56e64a..b84acdb4 100644 --- a/src/test_sup.erl +++ b/src/test_sup.erl @@ -50,7 +50,7 @@ test_supervisor_delayed_restart(SupPid) -> ok = exit_child(SupPid), timer:sleep(100), timeout = ping_child(SupPid), - timer:sleep(1010), + timer:sleep(1100), ok = ping_child(SupPid), passed. @@ -73,7 +73,7 @@ ping_child(SupPid) -> Ref = make_ref(), with_child_pid(SupPid, fun(ChildPid) -> ChildPid ! {ping, Ref, self()} end), receive {pong, Ref} -> ok - after 1000 -> timeout + after 1100 -> timeout end. exit_child(SupPid) -> -- cgit v1.2.1 From 0d619d9d9c4eaa5c4477f41aaf4818ceaab0b9c6 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 29 Nov 2012 12:57:37 +0000 Subject: reduce distance to OTP --- src/supervisor2.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index c5a16a9f..76dc4e83 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -74,7 +74,7 @@ start_child/2, restart_child/2, delete_child/2, terminate_child/2, which_children/1, count_children/1, - find_child/2, check_childspecs/1]). + find_child/2, check_childspecs/1]). %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -437,7 +437,7 @@ handle_call({terminate_child, Name}, _From, State) -> %%% The requests delete_child and restart_child are invalid for %%% simple_one_for_one supervisors. handle_call({_Req, _Data}, _From, State) when ?is_simple(State) -> - {reply, {error, State#state.strategy}, State}; + {reply, {error, simple_one_for_one}, State}; handle_call({start_child, ChildSpec}, _From, State) -> case check_childspec(ChildSpec) of @@ -777,6 +777,7 @@ restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(S error -> {ok, State} end; + restart_child(Pid, Reason, State) -> Children = State#state.children, case lists:keysearch(Pid, #child.pid, Children) of -- cgit v1.2.1 From 854b886d2e1f9f905abba6838b54dee61747ed8a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 29 Nov 2012 12:58:40 +0000 Subject: explain why we're not using lists:keyfind/3 --- src/supervisor2.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 76dc4e83..8a47c67d 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -780,6 +780,7 @@ restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(S restart_child(Pid, Reason, State) -> Children = State#state.children, + %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist case lists:keysearch(Pid, #child.pid, Children) of {value, Child} -> RestartType = Child#child.restart_type, -- cgit v1.2.1 From 76e832a6c6e451d049a1c5439251179444bbc966 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 29 Nov 2012 13:16:04 +0000 Subject: observe use_specs --- src/supervisor2.erl | 77 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 8a47c67d..8e01fe74 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -82,11 +82,12 @@ -export([try_again_restart/2]). %%-------------------------------------------------------------------------- - +-ifdef(use_specs). -export_type([child_spec/0, startchild_ret/0, strategy/0]). - +-endif. %%-------------------------------------------------------------------------- +-ifdef(use_specs). -type child() :: 'undefined' | pid(). -type child_id() :: term(). -type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. @@ -109,9 +110,11 @@ -type strategy() :: 'one_for_all' | 'one_for_one' | 'rest_for_one' | 'simple_one_for_one'. +-endif. %%-------------------------------------------------------------------------- +-ifdef(use_specs). -record(child, {% pid is undefined when child is not running pid = undefined :: child() | {restarting,pid()} | [pid()], name :: child_id(), @@ -121,11 +124,22 @@ child_type :: worker(), modules = [] :: modules()}). -type child_rec() :: #child{}. +-else. +-record(child, { + pid = undefined, + name, + mfargs, + restart_type, + shutdown, + child_type, + modules = []}). +-endif. -define(DICT, dict). -define(SETS, sets). -define(SET, set). +-ifdef(use_specs). -record(state, {name, strategy :: strategy(), children = [] :: [child_rec()], @@ -136,16 +150,28 @@ module, args}). -type state() :: #state{}. +-else. +-record(state, {name, + strategy, + children = [], + dynamics, + intensity, + period, + restarts = [], + module, + args}). +-endif. -define(is_simple(State), State#state.strategy =:= simple_one_for_one). +-ifdef(use_specs). -callback init(Args :: term()) -> {ok, {{RestartStrategy :: strategy(), MaxR :: non_neg_integer(), MaxT :: non_neg_integer()}, [ChildSpec :: child_spec()]}} | ignore. - +-endif. -define(restarting(_Pid_), {restarting,_Pid_}). %%% --------------------------------------------------- @@ -153,27 +179,31 @@ %%% Servers/processes should/could also be built using gen_server.erl. %%% SupName = {local, atom()} | {global, atom()}. %%% --------------------------------------------------- - +-ifdef(use_specs). -type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. -spec start_link(Module, Args) -> startlink_ret() when Module :: module(), Args :: term(). + +-endif. start_link(Mod, Args) -> gen_server:start_link(supervisor2, {self, Mod, Args}, []). +-ifdef(use_specs). -spec start_link(SupName, Module, Args) -> startlink_ret() when SupName :: sup_name(), Module :: module(), Args :: term(). +-endif. start_link(SupName, Mod, Args) -> gen_server:start_link(SupName, ?MODULE, {SupName, Mod, Args}, []). %%% --------------------------------------------------- %%% Interface functions. %%% --------------------------------------------------- - +-ifdef(use_specs). -type startchild_err() :: 'already_present' | {'already_started', Child :: child()} | term(). -type startchild_ret() :: {'ok', Child :: child()} @@ -183,9 +213,11 @@ start_link(SupName, Mod, Args) -> -spec start_child(SupRef, ChildSpec) -> startchild_ret() when SupRef :: sup_ref(), ChildSpec :: child_spec() | (List :: [term()]). +-endif. start_child(Supervisor, ChildSpec) -> call(Supervisor, {start_child, ChildSpec}). +-ifdef(use_specs). -spec restart_child(SupRef, Id) -> Result when SupRef :: sup_ref(), Id :: child_id(), @@ -194,14 +226,17 @@ start_child(Supervisor, ChildSpec) -> | {'error', Error}, Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one' | term(). +-endif. restart_child(Supervisor, Name) -> call(Supervisor, {restart_child, Name}). +-ifdef(use_specs). -spec delete_child(SupRef, Id) -> Result when SupRef :: sup_ref(), Id :: child_id(), Result :: 'ok' | {'error', Error}, Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one'. +-endif. delete_child(Supervisor, Name) -> call(Supervisor, {delete_child, Name}). @@ -211,24 +246,28 @@ delete_child(Supervisor, Name) -> %% Note that the child is *always* terminated in some %% way (maybe killed). %%----------------------------------------------------------------- - +-ifdef(use_specs). -spec terminate_child(SupRef, Id) -> Result when SupRef :: sup_ref(), Id :: pid() | child_id(), Result :: 'ok' | {'error', Error}, Error :: 'not_found' | 'simple_one_for_one'. +-endif. terminate_child(Supervisor, Name) -> call(Supervisor, {terminate_child, Name}). +-ifdef(use_specs). -spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when SupRef :: sup_ref(), Id :: child_id() | undefined, Child :: child() | 'restarting', Type :: worker(), Modules :: modules(). +-endif. which_children(Supervisor) -> call(Supervisor, which_children). +-ifdef(use_specs). -spec count_children(SupRef) -> PropListOfCounts when SupRef :: sup_ref(), PropListOfCounts :: [Count], @@ -236,15 +275,18 @@ which_children(Supervisor) -> | {active, ActiveProcessCount :: non_neg_integer()} | {supervisors, ChildSupervisorCount :: non_neg_integer()} |{workers, ChildWorkerCount :: non_neg_integer()}. +-endif. count_children(Supervisor) -> call(Supervisor, count_children). call(Supervisor, Req) -> gen_server:call(Supervisor, Req, infinity). +-ifdef(use_specs). -spec check_childspecs(ChildSpecs) -> Result when ChildSpecs :: [child_spec()], Result :: 'ok' | {'error', Error :: term()}. +-endif. check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> case check_startspec(ChildSpecs) of {ok, _} -> ok; @@ -254,15 +296,22 @@ check_childspecs(X) -> {error, {badarg, X}}. %%%----------------------------------------------------------------- %%% Called by timer:apply_after from restart/2 +-ifdef(use_specs). -spec try_again_restart(SupRef, Child) -> ok when SupRef :: sup_ref(), Child :: child_id() | pid(). +-endif. try_again_restart(Supervisor, Child) -> cast(Supervisor, {try_again_restart, Child}). cast(Supervisor, Req) -> gen_server:cast(Supervisor, Req). +-ifdef(use_specs). +-spec find_child(Supervisor, Name) -> [pid()] when + Supervisor :: sup_ref(), + Name :: child_id(). +-endif. find_child(Supervisor, Name) -> [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), Name1 =:= Name]. @@ -272,7 +321,7 @@ find_child(Supervisor, Name) -> %%% Initialize the supervisor. %%% %%% --------------------------------------------------- - +-ifdef(use_specs). -type init_sup_name() :: sup_name() | 'self'. -type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}} @@ -281,7 +330,7 @@ find_child(Supervisor, Name) -> -spec init({init_sup_name(), module(), [term()]}) -> {'ok', state()} | 'ignore' | {'stop', stop_rsn()}. - +-endif. init({SupName, Mod, Args}) -> process_flag(trap_exit, true), case Mod:init(Args) of @@ -581,9 +630,10 @@ count_child(#child{pid = Pid, child_type = supervisor}, %%% If a restart attempt failed, this message is sent via %%% timer:apply_after(0,...) in order to give gen_server the chance to %%% check it's inbox before trying again. +-ifdef(use_specs). -spec handle_cast({try_again_restart, child_id() | pid()}, state()) -> {'noreply', state()} | {stop, shutdown, state()}. - +-endif. handle_cast({try_again_restart,Pid}, #state{children=[Child]}=State) when ?is_simple(State) -> RT = Child#child.restart_type, @@ -618,9 +668,10 @@ handle_cast({try_again_restart,Name}, State) -> %% %% Take care of terminated children. %% +-ifdef(use_specs). -spec handle_info(term(), state()) -> {'noreply', state()} | {'stop', 'shutdown', state()}. - +-endif. handle_info({'EXIT', Pid, Reason}, State) -> case restart_child(Pid, Reason, State) of {ok, State1} -> @@ -650,8 +701,9 @@ handle_info(Msg, State) -> %% %% Terminate this server. %% +-ifdef(use_specs). -spec terminate(term(), state()) -> 'ok'. - +-endif. terminate(_Reason, #state{children=[Child]} = State) when ?is_simple(State) -> terminate_dynamic_children(Child, dynamics_db(Child#child.restart_type, State#state.dynamics), @@ -668,9 +720,10 @@ terminate(_Reason, State) -> %% NOTE: This requires that the init function of the call-back module %% does not have any side effects. %% +-ifdef(use_specs). -spec code_change(term(), state(), term()) -> {'ok', state()} | {'error', term()}. - +-endif. code_change(_, State, _) -> case (State#state.module):init(State#state.args) of {ok, {SupFlags, StartSpec}} -> -- cgit v1.2.1 From a8139d50954a567b3f2ad33fb34a04b32bf9cf90 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 29 Nov 2012 14:43:54 +0000 Subject: handle {permanent, Delay} for dynamic children --- src/supervisor2.erl | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 8e01fe74..b1a4a6c1 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -163,6 +163,10 @@ -endif. -define(is_simple(State), State#state.strategy =:= simple_one_for_one). +-define(is_permanent(R), ((R =:= permanent) orelse + (is_tuple(R) andalso + tuple_size(R) == 2 andalso + element(1, R) =:= permanent))). -ifdef(use_specs). -callback init(Args :: term()) -> @@ -1007,15 +1011,8 @@ do_terminate(Child, SupName) when is_pid(Child#child.pid) -> case shutdown(Child#child.pid, Child#child.shutdown) of ok -> ok; - {error, normal} -> - case Child#child.restart_type of - permanent -> - report_error(shutdown_error, normal, Child, SupName); - {permanent, _Delay} -> - report_error(shutdown_error, normal, Child, SupName); - _ -> - ok - end; + {error, normal} when not ?is_permanent(Child#child.restart_type) -> + ok; {error, OtherReason} -> report_error(shutdown_error, OtherReason, Child, SupName) end, @@ -1145,7 +1142,7 @@ monitor_dynamic_children(#child{restart_type=RType}, Dynamics) -> case monitor_child(P) of ok -> {?SETS:add_element(P, Pids), EStack}; - {error, normal} when RType =/= permanent -> + {error, normal} when ?is_permanent(RType) -> {Pids, EStack}; {error, Reason} -> {Pids, ?DICT:append(Reason, P, EStack)} @@ -1154,7 +1151,6 @@ monitor_dynamic_children(#child{restart_type=RType}, Dynamics) -> {Pids, EStack} end, {?SETS:new(), ?DICT:new()}, Dynamics). - wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) -> EStack; wait_dynamic_children(_Child, _Pids, 0, TRef, EStack) -> @@ -1185,7 +1181,7 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, TRef, EStack); - {'DOWN', _MRef, process, Pid, normal} when RType =/= permanent -> + {'DOWN', _MRef, process, Pid, normal} when ?is_permanent(RType) -> wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, TRef, EStack); -- cgit v1.2.1 From dcb5df1ee084f3c158812d480e469ae856115dc6 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 30 Nov 2012 10:55:31 +0000 Subject: oops - we mean 'not is_permanent' here --- src/supervisor2.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index b1a4a6c1..251d0d51 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -1142,7 +1142,7 @@ monitor_dynamic_children(#child{restart_type=RType}, Dynamics) -> case monitor_child(P) of ok -> {?SETS:add_element(P, Pids), EStack}; - {error, normal} when ?is_permanent(RType) -> + {error, normal} when not ?is_permanent(RType) -> {Pids, EStack}; {error, Reason} -> {Pids, ?DICT:append(Reason, P, EStack)} @@ -1181,7 +1181,7 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, TRef, EStack); - {'DOWN', _MRef, process, Pid, normal} when ?is_permanent(RType) -> + {'DOWN', _MRef, process, Pid, normal} when not ?is_permanent(RType) -> wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, TRef, EStack); -- cgit v1.2.1 From 1ad3a10e7ef1ec75bb367af02ebd0b447eaeae41 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 30 Nov 2012 11:01:16 +0000 Subject: document addition of find_child/2 --- src/supervisor2.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 251d0d51..0da00e58 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -3,7 +3,9 @@ %% %% 1) the module name is supervisor2 %% -%% 2) child specifications can contain, as the restart type, a tuple +%% 2) a find_child/2 utility function has been added +%% +%% 3) child specifications can contain, as the restart type, a tuple %% {permanent, Delay} | {transient, Delay} | {intrinsic, Delay} %% where Delay >= 0 (see point (4) below for intrinsic). The delay, %% in seconds, indicates what should happen if a child, upon being @@ -36,14 +38,14 @@ %% perspective it's a normal exit, whilst from supervisor's %% perspective, it's an abnormal exit. %% -%% 3) Added an 'intrinsic' restart type. Like the transient type, this +%% 4) Added an 'intrinsic' restart type. Like the transient type, this %% type means the child should only be restarted if the child exits %% abnormally. Unlike the transient type, if the child exits %% normally, the supervisor itself also exits normally. If the %% child is a supervisor and it exits normally (i.e. with reason of %% 'shutdown') then the child's parent also exits normally. %% -%% 4) normal, and {shutdown, _} exit reasons are all treated the same +%% 5) normal, and {shutdown, _} exit reasons are all treated the same %% (i.e. are regarded as normal exits) %% %% All modifications are (C) 2010-2012 VMware, Inc. -- cgit v1.2.1 From 427e44ed9712db32a020ffc2357bb04aef09fbc0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 4 Dec 2012 14:13:42 +0000 Subject: change comments to reflect merging with R15B-03 --- src/supervisor2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 0da00e58..da37ba39 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -1,4 +1,4 @@ -%% This file is a copy of supervisor.erl from the R13B-3 Erlang/OTP +%% This file is a copy of supervisor.erl from the R15B-3 Erlang/OTP %% distribution, with the following modifications: %% %% 1) the module name is supervisor2 -- cgit v1.2.1 From 265af7bac59bc588e53b9e59cd546922d6983e86 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 5 Dec 2012 12:04:08 +0000 Subject: cosmetic - reduce distance to OTP --- src/supervisor2.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 0da00e58..4e7cbe43 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -92,12 +92,12 @@ -ifdef(use_specs). -type child() :: 'undefined' | pid(). -type child_id() :: term(). --type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. --type modules() :: [module()] | 'dynamic'. --type delay() :: non_neg_integer(). --type restart() :: 'permanent' | 'transient' | 'temporary' | 'intrinsic' | {'permanent', delay()} | {'transient', delay()} | {'intrinsic', delay()}. +-type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. +-type modules() :: [module()] | 'dynamic'. +-type delay() :: non_neg_integer(). +-type restart() :: 'permanent' | 'transient' | 'temporary' | 'intrinsic' | {'permanent', delay()} | {'transient', delay()} | {'intrinsic', delay()}. -type shutdown() :: 'brutal_kill' | timeout(). --type worker() :: 'worker' | 'supervisor'. +-type worker() :: 'worker' | 'supervisor'. -type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. -type sup_ref() :: (Name :: atom()) | {Name :: atom(), Node :: node()} -- cgit v1.2.1 From 9803385de90f12f5d1dbdc1a6f716927d977b066 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 5 Dec 2012 12:04:59 +0000 Subject: use ?MODULE macro when appropriate --- src/supervisor2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 4e7cbe43..d4d9e106 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -195,7 +195,7 @@ -endif. start_link(Mod, Args) -> - gen_server:start_link(supervisor2, {self, Mod, Args}, []). + gen_server:start_link(?MODULE, {self, Mod, Args}, []). -ifdef(use_specs). -spec start_link(SupName, Module, Args) -> startlink_ret() when -- cgit v1.2.1 From 9d43c1ef36cae496223e5058afc927ebdf800ab4 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 5 Dec 2012 12:07:08 +0000 Subject: reduce distance to OTP - {temporary, Delay} is not a valid restart type --- src/supervisor2.erl | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index d4d9e106..e7e13672 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -393,12 +393,8 @@ init_dynamic(_State, StartSpec) -> start_children(Children, SupName) -> start_children(Children, [], SupName). start_children([Child|Chs], NChildren, SupName) -> - Restart = case Child#child.restart_type of - A when is_atom(A) -> A; - {N, _} when is_atom(N) -> N - end, case do_start_child(SupName, Child) of - {ok, undefined} when Restart =:= temporary -> + {ok, undefined} when Child#child.restart_type =:= temporary -> start_children(Chs, NChildren, SupName); {ok, Pid} -> start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); @@ -453,13 +449,9 @@ do_start_child_i(M, F, A) -> handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> Child = hd(State#state.children), #child{mfargs = {M, F, A}} = Child, - Restart = case Child#child.restart_type of - Name when is_atom(Name) -> Name; - {Type, _} when is_atom(Type) -> Type - end, Args = A ++ EArgs, case do_start_child_i(M, F, Args) of - {ok, undefined} when Restart =:= temporary -> + {ok, undefined} when Child#child.restart_type =:= temporary -> {reply, {ok, undefined}, State}; {ok, Pid} -> NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), -- cgit v1.2.1 From 369a5aeda3532ed9bbd1657c07a87c1cdda07a92 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 5 Dec 2012 12:08:09 +0000 Subject: oops - remember to guard specs for R12B03 compatibility --- src/supervisor2.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index e7e13672..36d00732 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -443,9 +443,10 @@ do_start_child_i(M, F, A) -> %%% Callback functions. %%% %%% --------------------------------------------------- +-ifdef(use_specs). -type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine -spec handle_call(call(), term(), state()) -> {'reply', term(), state()}. - +-endif. handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> Child = hd(State#state.children), #child{mfargs = {M, F, A}} = Child, -- cgit v1.2.1 From 9e7064134d5c71a208bd8e4cb2f13fd8e186fa12 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 5 Dec 2012 12:14:19 +0000 Subject: reduce distance to OTP - pattern match in case, not-found is always 'false' --- src/supervisor2.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 36d00732..8d6eaa37 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -834,10 +834,9 @@ restart_child(Pid, Reason, State) -> Children = State#state.children, %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist case lists:keysearch(Pid, #child.pid, Children) of - {value, Child} -> - RestartType = Child#child.restart_type, + {value, #child{restart_type = RestartType} = Child} -> do_restart(RestartType, Reason, Child, State); - _ -> + false -> {ok, State} end. -- cgit v1.2.1 From 598b0e0f907ea6e51241d7858eab04acc6830fbf Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 5 Dec 2012 12:15:56 +0000 Subject: infinity is always a valid shutdown reason --- src/supervisor2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 8d6eaa37..f0182822 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -1428,7 +1428,7 @@ validDelay(What) -> throw({invalid_delay, What}). validShutdown(Shutdown, _) when is_integer(Shutdown), Shutdown > 0 -> true; -validShutdown(infinity, supervisor) -> true; +validShutdown(infinity, _) -> true; validShutdown(brutal_kill, _) -> true; validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}). -- cgit v1.2.1 From 2052456394635806458b9d73bc5dc7e38c5cb0fd Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 7 Dec 2012 13:15:38 +0000 Subject: remove compiler warning --- src/supervisor2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index a1f758ff..89e78703 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -688,7 +688,7 @@ handle_info({delayed_restart, {RestartType, Reason, Child}}, State) -> {value, Child1} -> {ok, NState} = do_restart(RestartType, Reason, Child1, State), {noreply, NState}; - What -> + _What -> {noreply, State} end; -- cgit v1.2.1 From 68ede92e82dcdfc73b36f0e5f506da5e43c4f044 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 10 Jan 2013 15:12:46 +0000 Subject: send connection.{blocked,unblocked} --- src/rabbit_alarm.erl | 7 +++--- src/rabbit_reader.erl | 67 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 55 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index d7d4d82a..55b71820 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -37,7 +37,7 @@ -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). -spec(start/0 :: () -> 'ok'). -spec(stop/0 :: () -> 'ok'). --spec(register/2 :: (pid(), rabbit_types:mfargs()) -> boolean()). +-spec(register/2 :: (pid(), rabbit_types:mfargs()) -> [atom()]). -spec(set_alarm/1 :: (any()) -> 'ok'). -spec(clear_alarm/1 :: (any()) -> 'ok'). -spec(on_node_up/1 :: (node()) -> 'ok'). @@ -94,8 +94,9 @@ init([]) -> alarmed_nodes = dict:new(), alarms = []}}. -handle_call({register, Pid, HighMemMFA}, State) -> - {ok, 0 < dict:size(State#alarms.alarmed_nodes), +handle_call({register, Pid, HighMemMFA}, State = #alarms{alarmed_nodes = AN}) -> + Vs = [V || {_, V} <- dict:to_list(AN)], + {ok, lists:usort(lists:append(Vs)), internal_register(Pid, HighMemMFA, State)}; handle_call(get_alarms, State = #alarms{alarms = Alarms}) -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 13e8feff..734764a7 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -44,7 +44,8 @@ client_properties, capabilities, auth_mechanism, auth_state}). --record(throttle, {conserve_resources, last_blocked_by, last_blocked_at}). +-record(throttle, {conserve_resources, last_blocked_by, last_blocked_at, + blocked_sent}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, last_blocked_by, last_blocked_age, @@ -137,8 +138,8 @@ info(Pid, Items) -> force_event_refresh(Pid) -> gen_server:cast(Pid, force_event_refresh). -conserve_resources(Pid, _Source, Conserve) -> - Pid ! {conserve_resources, Conserve}, +conserve_resources(Pid, Source, Conserve) -> + Pid ! {conserve_resources, Source, Conserve}, ok. server_properties(Protocol) -> @@ -235,9 +236,10 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, buf = [], buf_len = 0, throttle = #throttle{ - conserve_resources = false, - last_blocked_by = none, - last_blocked_at = never}}, + conserve_resources = [], + last_blocked_by = none, + last_blocked_at = never, + blocked_sent = false}}, try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( @@ -293,9 +295,12 @@ mainloop(Deb, State = #v1{sock = Sock, buf = Buf, buf_len = BufLen}) -> {other, Other} -> handle_other(Other, Deb, State) end. -handle_other({conserve_resources, Conserve}, Deb, +handle_other({conserve_resources, Source, Conserve}, Deb, State = #v1{throttle = Throttle}) -> - Throttle1 = Throttle#throttle{conserve_resources = Conserve}, + Throttle1 = Throttle#throttle{conserve_resources = case Conserve of + true -> [Source]; + false -> [] + end}, recvloop(Deb, control_throttle(State#v1{throttle = Throttle1})); handle_other({channel_closing, ChPid}, Deb, State) -> ok = rabbit_channel:ready_for_close(ChPid), @@ -380,30 +385,60 @@ terminate(_Explanation, State) -> {force, State}. control_throttle(State = #v1{connection_state = CS, throttle = Throttle}) -> - case {CS, (Throttle#throttle.conserve_resources orelse + case {CS, ((Throttle#throttle.conserve_resources =/= []) orelse credit_flow:blocked())} of {running, true} -> State#v1{connection_state = blocking}; {blocking, false} -> State#v1{connection_state = running}; {blocked, false} -> ok = rabbit_heartbeat:resume_monitor( State#v1.heartbeater), - State#v1{connection_state = running}; + maybe_send_unblocked(State), + State#v1{connection_state = running, + throttle = Throttle#throttle{ + blocked_sent = false}}; {blocked, true} -> State#v1{throttle = update_last_blocked_by( Throttle)}; {_, _} -> State end. -maybe_block(State = #v1{connection_state = blocking, throttle = Throttle}) -> +maybe_block(State = #v1{connection_state = blocking, + throttle = Throttle}) -> ok = rabbit_heartbeat:pause_monitor(State#v1.heartbeater), + Sent = maybe_send_blocked(State), State#v1{connection_state = blocked, throttle = update_last_blocked_by( - Throttle#throttle{last_blocked_at = erlang:now()})}; + Throttle#throttle{last_blocked_at = erlang:now(), + blocked_sent = Sent})}; maybe_block(State) -> State. -update_last_blocked_by(Throttle = #throttle{conserve_resources = true}) -> - Throttle#throttle{last_blocked_by = resource}; -update_last_blocked_by(Throttle = #throttle{conserve_resources = false}) -> - Throttle#throttle{last_blocked_by = flow}. +maybe_send_blocked(#v1{throttle = #throttle{conserve_resources = []}}) -> + false; +maybe_send_blocked(#v1{throttle = #throttle{conserve_resources = CR}, + connection = #connection{ + protocol = Protocol, + capabilities = Capabilities}, + sock = Sock}) -> + case rabbit_misc:table_lookup(Capabilities, <<"connection.blocked">>) of + {bool, true} -> + RStr = string:join([atom_to_list(A) || A <- CR], " & "), + Reason = list_to_binary(rabbit_misc:format("low on ~s", [RStr])), + ok = send_on_channel0(Sock, #'connection.blocked'{reason = Reason}, + Protocol), + true; + _ -> + false + end. + +maybe_send_unblocked(#v1{throttle = #throttle{blocked_sent = false}}) -> + ok; +maybe_send_unblocked(#v1{connection = #connection{protocol = Protocol}, + sock = Sock}) -> + ok = send_on_channel0(Sock, #'connection.unblocked'{}, Protocol). + +update_last_blocked_by(Throttle = #throttle{conserve_resources = []}) -> + Throttle#throttle{last_blocked_by = flow}; +update_last_blocked_by(Throttle) -> + Throttle#throttle{last_blocked_by = resource}. %%-------------------------------------------------------------------------- %% error handling / termination -- cgit v1.2.1 From 711290d644b3e43e0805ced7b83747b7520b8633 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 22 Jan 2013 12:16:09 +0000 Subject: revert spurious changes to test timings --- src/test_sup.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/test_sup.erl b/src/test_sup.erl index b84acdb4..955c44e6 100644 --- a/src/test_sup.erl +++ b/src/test_sup.erl @@ -50,7 +50,7 @@ test_supervisor_delayed_restart(SupPid) -> ok = exit_child(SupPid), timer:sleep(100), timeout = ping_child(SupPid), - timer:sleep(1100), + timer:sleep(1000), ok = ping_child(SupPid), passed. @@ -73,7 +73,7 @@ ping_child(SupPid) -> Ref = make_ref(), with_child_pid(SupPid, fun(ChildPid) -> ChildPid ! {ping, Ref, self()} end), receive {pong, Ref} -> ok - after 1100 -> timeout + after 1000 -> timeout end. exit_child(SupPid) -> -- cgit v1.2.1 From 488c3781a3b9211078b8c11295333e159fce7a02 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 27 Jan 2013 19:28:51 +0000 Subject: move bits of docs around in order to eliminate "forward references" to new features --- src/supervisor2.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 89e78703..cbca993c 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -5,7 +5,14 @@ %% %% 2) a find_child/2 utility function has been added %% -%% 3) child specifications can contain, as the restart type, a tuple +%% 3) Added an 'intrinsic' restart type. Like the transient type, this +%% type means the child should only be restarted if the child exits +%% abnormally. Unlike the transient type, if the child exits +%% normally, the supervisor itself also exits normally. If the +%% child is a supervisor and it exits normally (i.e. with reason of +%% 'shutdown') then the child's parent also exits normally. +%% +%% 4) child specifications can contain, as the restart type, a tuple %% {permanent, Delay} | {transient, Delay} | {intrinsic, Delay} %% where Delay >= 0 (see point (4) below for intrinsic). The delay, %% in seconds, indicates what should happen if a child, upon being @@ -38,13 +45,6 @@ %% perspective it's a normal exit, whilst from supervisor's %% perspective, it's an abnormal exit. %% -%% 4) Added an 'intrinsic' restart type. Like the transient type, this -%% type means the child should only be restarted if the child exits -%% abnormally. Unlike the transient type, if the child exits -%% normally, the supervisor itself also exits normally. If the -%% child is a supervisor and it exits normally (i.e. with reason of -%% 'shutdown') then the child's parent also exits normally. -%% %% 5) normal, and {shutdown, _} exit reasons are all treated the same %% (i.e. are regarded as normal exits) %% -- cgit v1.2.1 From 0f26c7ae57c04790b3275d85bf45b5d3717bca19 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 27 Jan 2013 19:29:15 +0000 Subject: cosmetic - indent like OTP --- src/supervisor2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index cbca993c..a762defa 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -76,7 +76,7 @@ start_child/2, restart_child/2, delete_child/2, terminate_child/2, which_children/1, count_children/1, - find_child/2, check_childspecs/1]). + find_child/2, check_childspecs/1]). %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, -- cgit v1.2.1 From 4c8ed37e06d15e5c62b211b5d129ed72059d0b51 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 27 Jan 2013 19:29:40 +0000 Subject: move find_child implementation to a better place --- src/supervisor2.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index a762defa..1f1f9246 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -285,6 +285,15 @@ which_children(Supervisor) -> count_children(Supervisor) -> call(Supervisor, count_children). +-ifdef(use_specs). +-spec find_child(Supervisor, Name) -> [pid()] when + Supervisor :: sup_ref(), + Name :: child_id(). +-endif. +find_child(Supervisor, Name) -> + [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), + Name1 =:= Name]. + call(Supervisor, Req) -> gen_server:call(Supervisor, Req, infinity). @@ -313,15 +322,6 @@ try_again_restart(Supervisor, Child) -> cast(Supervisor, Req) -> gen_server:cast(Supervisor, Req). --ifdef(use_specs). --spec find_child(Supervisor, Name) -> [pid()] when - Supervisor :: sup_ref(), - Name :: child_id(). --endif. -find_child(Supervisor, Name) -> - [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), - Name1 =:= Name]. - %%% --------------------------------------------------- %%% %%% Initialize the supervisor. -- cgit v1.2.1 From 33a40ada5818f20c45dddfaa98911af1855e717d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 1 Feb 2013 11:02:19 +0000 Subject: Ensure supervisor try_again_restart observes delayed restart --- src/supervisor2.erl | 71 +++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 1f1f9246..7f04536a 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -81,7 +81,7 @@ %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([try_again_restart/2]). +-export([try_again_restart/3]). %%-------------------------------------------------------------------------- -ifdef(use_specs). @@ -312,12 +312,13 @@ check_childspecs(X) -> {error, {badarg, X}}. %%%----------------------------------------------------------------- %%% Called by timer:apply_after from restart/2 -ifdef(use_specs). --spec try_again_restart(SupRef, Child) -> ok when +-spec try_again_restart(SupRef, Child, Reason) -> ok when SupRef :: sup_ref(), - Child :: child_id() | pid(). + Child :: child_id() | pid(), + Reason :: term(). -endif. -try_again_restart(Supervisor, Child) -> - cast(Supervisor, {try_again_restart, Child}). +try_again_restart(Supervisor, Child, Reason) -> + cast(Supervisor, {try_again_restart, Child, Reason}). cast(Supervisor, Req) -> gen_server:cast(Supervisor, Req). @@ -362,7 +363,7 @@ init_children(State, StartSpec) -> case start_children(Children, SupName) of {ok, NChildren} -> {ok, State#state{children = NChildren}}; - {error, NChildren} -> + {error, _, NChildren} -> terminate_children(NChildren, SupName), {stop, shutdown} end; @@ -402,7 +403,7 @@ start_children([Child|Chs], NChildren, SupName) -> start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); {error, Reason} -> report_error(start_error, Reason, Child, SupName), - {error, lists:reverse(Chs) ++ [Child | NChildren]} + {error, Reason, lists:reverse(Chs) ++ [Child | NChildren]} end; start_children([], NChildren, _SupName) -> {ok, NChildren}. @@ -630,10 +631,10 @@ count_child(#child{pid = Pid, child_type = supervisor}, %%% timer:apply_after(0,...) in order to give gen_server the chance to %%% check it's inbox before trying again. -ifdef(use_specs). --spec handle_cast({try_again_restart, child_id() | pid()}, state()) -> +-spec handle_cast({try_again_restart, child_id() | pid(), term()}, state()) -> {'noreply', state()} | {stop, shutdown, state()}. -endif. -handle_cast({try_again_restart,Pid}, #state{children=[Child]}=State) +handle_cast({try_again_restart,Pid,Reason}, #state{children=[Child]}=State) when ?is_simple(State) -> RT = Child#child.restart_type, RPid = restarting(Pid), @@ -641,7 +642,7 @@ handle_cast({try_again_restart,Pid}, #state{children=[Child]}=State) {ok, Args} -> {M, F, _} = Child#child.mfargs, NChild = Child#child{pid = RPid, mfargs = {M, F, Args}}, - case restart(NChild,State) of + case restart_child(NChild,Reason,State) of {ok, State1} -> {noreply, State1}; {shutdown, State1} -> @@ -651,10 +652,10 @@ handle_cast({try_again_restart,Pid}, #state{children=[Child]}=State) {noreply, State} end; -handle_cast({try_again_restart,Name}, State) -> +handle_cast({try_again_restart,Name,Reason}, State) -> case lists:keyfind(Name,#child.name,State#state.children) of Child = #child{pid=?restarting(_)} -> - case restart(Child,State) of + case restart_child(Child,Reason,State) of {ok, State1} -> {noreply, State1}; {shutdown, State1} -> @@ -867,27 +868,27 @@ do_restart(temporary, Reason, Child, State) -> {ok, NState}. do_restart_delay({RestartType, Delay}, Reason, Child, State) -> - case restart1(Child, State) of + case add_restart(State) of {ok, NState} -> + restart(NState#state.strategy, Child, NState); + {try_again, Reason, NState} -> + %% See restart/2 for an explanation of try_again_restart + Id = if ?is_simple(State) -> Child#child.pid; + true -> Child#child.name + end, + timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]), {ok, NState}; - {terminate, NState} -> - _TRef = erlang:send_after(trunc(Delay*1000), self(), - {delayed_restart, - {{RestartType, Delay}, Reason, Child}}), - {ok, state_del_child(Child, NState)} - end. - -restart1(Child, State) -> - case add_restart(State) of - {ok, NState} -> - restart(NState#state.strategy, Child, NState); - {terminate, _NState} -> + {terminate, _NState} -> %% we've reached the max restart intensity, but the %% add_restart will have added to the restarts %% field. Given we don't want to die here, we need to go %% back to the old restarts field otherwise we'll never - %% attempt to restart later. - {terminate, State} + %% attempt to restart later, which is why we ignore + %% NState for this clause. + _TRef = erlang:send_after(trunc(Delay*1000), self(), + {delayed_restart, + {{RestartType, Delay}, Reason, Child}}), + {ok, state_del_child(Child, State)} end. del_child_and_maybe_shutdown(intrinsic, Child, State) -> @@ -901,7 +902,7 @@ restart(Child, State) -> case add_restart(State) of {ok, NState} -> case restart(NState#state.strategy, Child, NState) of - {try_again,NState2} -> + {try_again, Reason, NState2} -> %% Leaving control back to gen_server before %% trying again. This way other incoming requsts %% for the supervisor can be handled - e.g. a @@ -910,7 +911,7 @@ restart(Child, State) -> Id = if ?is_simple(State) -> Child#child.pid; true -> Child#child.name end, - timer:apply_after(0,?MODULE,try_again_restart,[self(),Id]), + timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]), {ok,NState2}; Other -> Other @@ -936,7 +937,7 @@ restart(simple_one_for_one, Child, State) -> NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A, Dynamics)}, report_error(start_error, Error, Child, State#state.name), - {try_again, NState} + {try_again, Error, NState} end; restart(one_for_one, Child, State) -> OldPid = Child#child.pid, @@ -950,7 +951,7 @@ restart(one_for_one, Child, State) -> {error, Reason} -> NState = replace_child(Child#child{pid = restarting(OldPid)}, State), report_error(start_error, Reason, Child, State#state.name), - {try_again, NState} + {try_again, Reason, NState} end; restart(rest_for_one, Child, State) -> {ChAfter, ChBefore} = split_child(Child#child.pid, State#state.children), @@ -958,10 +959,10 @@ restart(rest_for_one, Child, State) -> case start_children(ChAfter2, State#state.name) of {ok, ChAfter3} -> {ok, State#state{children = ChAfter3 ++ ChBefore}}; - {error, ChAfter3} -> + {error, Reason, ChAfter3} -> NChild = Child#child{pid=restarting(Child#child.pid)}, NState = State#state{children = ChAfter3 ++ ChBefore}, - {try_again, replace_child(NChild,NState)} + {try_again, Reason, replace_child(NChild,NState)} end; restart(one_for_all, Child, State) -> Children1 = del_child(Child#child.pid, State#state.children), @@ -969,10 +970,10 @@ restart(one_for_all, Child, State) -> case start_children(Children2, State#state.name) of {ok, NChs} -> {ok, State#state{children = NChs}}; - {error, NChs} -> + {error, Reason, NChs} -> NChild = Child#child{pid=restarting(Child#child.pid)}, NState = State#state{children = NChs}, - {try_again, replace_child(NChild,NState)} + {try_again, Reason, replace_child(NChild,NState)} end. restarting(Pid) when is_pid(Pid) -> ?restarting(Pid); -- cgit v1.2.1 From b60a1362ea4739a4f0fc271ad7a29a2db18e9aee Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 1 Feb 2013 14:02:49 +0000 Subject: avoid badmatch in handle_info/delayed_restart --- src/supervisor2.erl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 7f04536a..693e0b6d 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -682,13 +682,11 @@ handle_info({'EXIT', Pid, Reason}, State) -> handle_info({delayed_restart, {RestartType, Reason, Child}}, State) when ?is_simple(State) -> - {ok, NState} = do_restart(RestartType, Reason, Child, State), - {noreply, NState}; + delayed_restart(RestartType, Reason, Child, State); handle_info({delayed_restart, {RestartType, Reason, Child}}, State) -> case get_child(Child#child.name, State) of {value, Child1} -> - {ok, NState} = do_restart(RestartType, Reason, Child1, State), - {noreply, NState}; + delayed_restart(RestartType, Reason, Child1, State); _What -> {noreply, State} end; @@ -698,6 +696,12 @@ handle_info(Msg, State) -> [Msg]), {noreply, State}. +delayed_restart(RestartType, Reason, Child, State) -> + case do_restart(RestartType, Reason, Child, State) of + {ok, NState} -> {noreply, NState}; + {try_again, _, NState} -> {noreply, NState} + end. + %% %% Terminate this server. %% -- cgit v1.2.1 From 84da16816bd189a730398e05706f7b89f8c6f544 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 20 Feb 2013 14:27:17 +0000 Subject: revert test timings back to exactly what's on default --- src/test_sup.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/test_sup.erl b/src/test_sup.erl index 955c44e6..6a56e64a 100644 --- a/src/test_sup.erl +++ b/src/test_sup.erl @@ -50,7 +50,7 @@ test_supervisor_delayed_restart(SupPid) -> ok = exit_child(SupPid), timer:sleep(100), timeout = ping_child(SupPid), - timer:sleep(1000), + timer:sleep(1010), ok = ping_child(SupPid), passed. -- cgit v1.2.1 From 30d4a9ae902c8a8ee9593a4ff603bda06563fbd0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 4 Mar 2013 14:20:33 +0000 Subject: rebase with R16B --- src/supervisor2.erl | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 693e0b6d..3f807573 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -186,7 +186,9 @@ %%% SupName = {local, atom()} | {global, atom()}. %%% --------------------------------------------------- -ifdef(use_specs). --type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). +-type startlink_err() :: {'already_started', pid()} + | {'shutdown', term()} + | term(). -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. -spec start_link(Module, Args) -> startlink_ret() when @@ -331,8 +333,10 @@ cast(Supervisor, Req) -> -ifdef(use_specs). -type init_sup_name() :: sup_name() | 'self'. --type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}} - | {'bad_start_spec', term()} | {'start_spec', term()} +-type stop_rsn() :: {'shutdown', term()} + | {'bad_return', {module(),'init', term()}} + | {'bad_start_spec', term()} + | {'start_spec', term()} | {'supervisor_data', term()}. -spec init({init_sup_name(), module(), [term()]}) -> @@ -363,9 +367,9 @@ init_children(State, StartSpec) -> case start_children(Children, SupName) of {ok, NChildren} -> {ok, State#state{children = NChildren}}; - {error, _, NChildren} -> + {error, NChildren, Reason} -> terminate_children(NChildren, SupName), - {stop, shutdown} + {stop, {shutdown, Reason}} end; Error -> {stop, {start_spec, Error}} @@ -385,9 +389,9 @@ init_dynamic(_State, StartSpec) -> %% Func: start_children/2 %% Args: Children = [child_rec()] in start order %% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} -%% Purpose: Start all children. The new list contains #child's +%% Purpose: Start all children. The new list contains #child's %% with pids. -%% Returns: {ok, NChildren} | {error, NChildren} +%% Returns: {ok, NChildren} | {error, NChildren, Reason} %% NChildren = [child_rec()] in termination order (reversed %% start order) %%----------------------------------------------------------------- @@ -403,7 +407,8 @@ start_children([Child|Chs], NChildren, SupName) -> start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); {error, Reason} -> report_error(start_error, Reason, Child, SupName), - {error, Reason, lists:reverse(Chs) ++ [Child | NChildren]} + {error, lists:reverse(Chs) ++ [Child | NChildren], + {failed_to_start_child,Child#child.name,Reason}} end; start_children([], NChildren, _SupName) -> {ok, NChildren}. @@ -963,7 +968,7 @@ restart(rest_for_one, Child, State) -> case start_children(ChAfter2, State#state.name) of {ok, ChAfter3} -> {ok, State#state{children = ChAfter3 ++ ChBefore}}; - {error, Reason, ChAfter3} -> + {error, ChAfter3, Reason} -> NChild = Child#child{pid=restarting(Child#child.pid)}, NState = State#state{children = ChAfter3 ++ ChBefore}, {try_again, Reason, replace_child(NChild,NState)} @@ -974,7 +979,7 @@ restart(one_for_all, Child, State) -> case start_children(Children2, State#state.name) of {ok, NChs} -> {ok, State#state{children = NChs}}; - {error, Reason, NChs} -> + {error, NChs, Reason} -> NChild = Child#child{pid=restarting(Child#child.pid)}, NState = State#state{children = NChs}, {try_again, Reason, replace_child(NChild,NState)} -- cgit v1.2.1 From 971559d454c8873afdcc1a66fc1267fc0c26480c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 6 Mar 2013 03:52:01 +0000 Subject: correct comment --- src/supervisor2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 3f807573..67dbab76 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -1,4 +1,4 @@ -%% This file is a copy of supervisor.erl from the R15B-3 Erlang/OTP +%% This file is a copy of supervisor.erl from the R16B Erlang/OTP %% distribution, with the following modifications: %% %% 1) the module name is supervisor2 -- cgit v1.2.1 From d8b30c8d9baf22bdce5644b2af854f2edb080738 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 6 Mar 2013 20:29:50 +0000 Subject: Refactor try_again restart handling --- src/supervisor2.erl | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 3f807573..719c8b3c 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -703,8 +703,8 @@ handle_info(Msg, State) -> delayed_restart(RestartType, Reason, Child, State) -> case do_restart(RestartType, Reason, Child, State) of - {ok, NState} -> {noreply, NState}; - {try_again, _, NState} -> {noreply, NState} + {ok, NState} -> {noreply, NState}; + Other -> Other end. %% @@ -879,14 +879,7 @@ do_restart(temporary, Reason, Child, State) -> do_restart_delay({RestartType, Delay}, Reason, Child, State) -> case add_restart(State) of {ok, NState} -> - restart(NState#state.strategy, Child, NState); - {try_again, Reason, NState} -> - %% See restart/2 for an explanation of try_again_restart - Id = if ?is_simple(State) -> Child#child.pid; - true -> Child#child.name - end, - timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]), - {ok, NState}; + maybe_restart(NState#state.strategy, Child, NState); {terminate, _NState} -> %% we've reached the max restart intensity, but the %% add_restart will have added to the restarts @@ -910,27 +903,30 @@ del_child_and_maybe_shutdown(_, Child, State) -> restart(Child, State) -> case add_restart(State) of {ok, NState} -> - case restart(NState#state.strategy, Child, NState) of - {try_again, Reason, NState2} -> - %% Leaving control back to gen_server before - %% trying again. This way other incoming requsts - %% for the supervisor can be handled - e.g. a - %% shutdown request for the supervisor or the - %% child. - Id = if ?is_simple(State) -> Child#child.pid; - true -> Child#child.name - end, - timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]), - {ok,NState2}; - Other -> - Other - end; + maybe_restart(NState#state.strategy, Child, NState); {terminate, NState} -> report_error(shutdown, reached_max_restart_intensity, Child, State#state.name), {shutdown, remove_child(Child, NState)} end. +maybe_restart(Strategy, Child, State) -> + case restart(Strategy, Child, State) of + {try_again, Reason, NState2} -> + %% Leaving control back to gen_server before + %% trying again. This way other incoming requsts + %% for the supervisor can be handled - e.g. a + %% shutdown request for the supervisor or the + %% child. + Id = if ?is_simple(State) -> Child#child.pid; + true -> Child#child.name + end, + timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]), + {ok,NState2}; + Other -> + Other + end. + restart(simple_one_for_one, Child, State) -> #child{pid = OldPid, mfargs = {M, F, A}} = Child, Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type, -- cgit v1.2.1 From ad6585ac8fe190ec01fc76bbee76c7a845b016d6 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 8 Mar 2013 16:22:19 +0000 Subject: Align error reporting and restart handling with OTP --- src/supervisor2.erl | 106 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index e5a05f66..aba40626 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -141,6 +141,9 @@ -define(SETS, sets). -define(SET, set). +-define(is_explicit_restart(R), + R == {shutdown, restart}). + -ifdef(use_specs). -record(state, {name, strategy :: strategy(), @@ -850,31 +853,79 @@ restart_child(Pid, Reason, State) -> {ok, State} end. -do_restart({permanent = RestartType, Delay}, Reason, Child, State) -> - do_restart_delay({RestartType, Delay}, Reason, Child, State); -do_restart(permanent, Reason, Child, State) -> - report_error(child_terminated, Reason, Child, State#state.name), - restart(Child, State); -do_restart(Type, normal, Child, State) -> - del_child_and_maybe_shutdown(Type, Child, State); -do_restart({RestartType, Delay}, {shutdown, restart} = Reason, Child, State) - when RestartType =:= transient orelse RestartType =:= intrinsic -> - do_restart_delay({RestartType, Delay}, Reason, Child, State); -do_restart(Type, {shutdown, _}, Child, State) -> - del_child_and_maybe_shutdown(Type, Child, State); -do_restart(Type, shutdown, Child = #child{child_type = supervisor}, State) -> - del_child_and_maybe_shutdown(Type, Child, State); -do_restart({RestartType, Delay}, Reason, Child, State) - when RestartType =:= transient orelse RestartType =:= intrinsic -> - do_restart_delay({RestartType, Delay}, Reason, Child, State); -do_restart(Type, Reason, Child, State) when Type =:= transient orelse - Type =:= intrinsic -> - report_error(child_terminated, Reason, Child, State#state.name), +do_restart(RestartType, Reason, Child, State) -> + maybe_report_error(RestartType, Reason, Child, State), + handle_restart(RestartType, Reason, Child, State). + +maybe_report_error(permanent, Reason, Child, State) -> + report_child_termination(Reason, Child, State); +maybe_report_error({permanent, _}, Reason, Child, State) -> + report_child_termination(Reason, Child, State); +maybe_report_error(_Type, Reason, Child, State) -> + case is_abnormal_termination(Reason) of + true -> report_child_termination(Reason, Child, State); + false -> ok + end. + +report_child_termination(Reason, Child, State) -> + report_error(child_terminated, Reason, Child, State#state.name). + +handle_restart(permanent, _Reason, Child, State) -> restart(Child, State); -do_restart(temporary, Reason, Child, State) -> - report_error(child_terminated, Reason, Child, State#state.name), - NState = state_del_child(Child, State), - {ok, NState}. +handle_restart(transient, Reason, Child, State) -> + restart_if_explicit_or_abnormal(fun restart/2, + fun delete_child_and_continue/2, + Reason, Child, State); +handle_restart(intrinsic, Reason, Child, State) -> + restart_if_explicit_or_abnormal(fun restart/2, + fun delete_child_and_stop/2, + Reason, Child, State); +handle_restart(temporary, _Reason, Child, State) -> + delete_child_and_continue(Child, State); +handle_restart({_RestartType, _Delay}=Restart, Reason, Child, State) -> + handle_delayed_restart(Restart, Reason, Child, State). + +handle_delayed_restart({permanent, _Delay}=Restart, Reason, Child, State) -> + do_restart_delay(Restart, Reason, Child, State); +handle_delayed_restart({RestartType, _Delay}=Restart, Reason, Child, State) + when ?is_explicit_restart(Reason) andalso + (RestartType =:= transient orelse + RestartType =:= intrinsic) -> + do_restart_delay(Restart, Reason, Child, State); +handle_delayed_restart({transient, _Delay}=Restart, Reason, Child, State) -> + restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason), + fun delete_child_and_continue/2, + Reason, Child, State); +handle_delayed_restart({intrinsic, _Delay}=Restart, Reason, Child, State) -> + restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason), + fun delete_child_and_stop/2, + Reason, Child, State). + +restart_if_explicit_or_abnormal(RestartHow, _Otherwise, Reason, Child, State) + when is_function(RestartHow, 2) andalso + ?is_explicit_restart(Reason) -> + RestartHow(Child, State); +restart_if_explicit_or_abnormal(RestartHow, Otherwise, Reason, Child, State) + when is_function(RestartHow, 2) andalso + is_function(Otherwise, 2) -> + case is_abnormal_termination(Reason) of + true -> RestartHow(Child, State); + false -> Otherwise(Child, State) + end. + +defer_to_restart_delay(Restart, Reason) -> + fun(Child, State) -> do_restart_delay(Restart, Reason, Child, State) end. + +delete_child_and_continue(Child, State) -> + {ok, state_del_child(Child, State)}. + +delete_child_and_stop(Child, State) -> + {shutdown, state_del_child(Child, State)}. + +is_abnormal_termination(normal) -> false; +is_abnormal_termination(shutdown) -> false; +is_abnormal_termination({shutdown, _}) -> false; +is_abnormal_termination(_Other) -> true. do_restart_delay({RestartType, Delay}, Reason, Child, State) -> case add_restart(State) of @@ -893,13 +944,6 @@ do_restart_delay({RestartType, Delay}, Reason, Child, State) -> {ok, state_del_child(Child, State)} end. -del_child_and_maybe_shutdown(intrinsic, Child, State) -> - {shutdown, state_del_child(Child, State)}; -del_child_and_maybe_shutdown({intrinsic, _Delay}, Child, State) -> - {shutdown, state_del_child(Child, State)}; -del_child_and_maybe_shutdown(_, Child, State) -> - {ok, state_del_child(Child, State)}. - restart(Child, State) -> case add_restart(State) of {ok, NState} -> -- cgit v1.2.1 From fe1f803340caa83e9737399d9bb0148c9d8a9d10 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 8 Mar 2013 20:23:10 +0000 Subject: Translate return from do_restart properly --- src/supervisor2.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index aba40626..ff519acd 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -706,8 +706,8 @@ handle_info(Msg, State) -> delayed_restart(RestartType, Reason, Child, State) -> case do_restart(RestartType, Reason, Child, State) of - {ok, NState} -> {noreply, NState}; - Other -> Other + {ok, NState} -> {noreply, NState}; + {shutdown, State2} -> {stop, shutdown, State2} end. %% -- cgit v1.2.1 From e4b8ffb341f1141ebd897b00e43a0640a8fab693 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 11 Mar 2013 14:34:39 +0000 Subject: track repeated attempts to restart properly --- src/supervisor2.erl | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index ff519acd..a181e7d4 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -650,25 +650,15 @@ handle_cast({try_again_restart,Pid,Reason}, #state{children=[Child]}=State) {ok, Args} -> {M, F, _} = Child#child.mfargs, NChild = Child#child{pid = RPid, mfargs = {M, F, Args}}, - case restart_child(NChild,Reason,State) of - {ok, State1} -> - {noreply, State1}; - {shutdown, State1} -> - {stop, shutdown, State1} - end; + try_restart(Child#child.restart_type, Reason, NChild, State); error -> {noreply, State} end; handle_cast({try_again_restart,Name,Reason}, State) -> case lists:keyfind(Name,#child.name,State#state.children) of - Child = #child{pid=?restarting(_)} -> - case restart_child(Child,Reason,State) of - {ok, State1} -> - {noreply, State1}; - {shutdown, State1} -> - {stop, shutdown, State1} - end; + Child = #child{pid=?restarting(_), restart_type=RestartType} -> + try_restart(RestartType, Reason, Child, State); _ -> {noreply,State} end. @@ -690,11 +680,11 @@ handle_info({'EXIT', Pid, Reason}, State) -> handle_info({delayed_restart, {RestartType, Reason, Child}}, State) when ?is_simple(State) -> - delayed_restart(RestartType, Reason, Child, State); + try_restart(RestartType, Reason, Child, State); handle_info({delayed_restart, {RestartType, Reason, Child}}, State) -> case get_child(Child#child.name, State) of {value, Child1} -> - delayed_restart(RestartType, Reason, Child1, State); + try_restart(RestartType, Reason, Child1, State); _What -> {noreply, State} end; @@ -704,12 +694,6 @@ handle_info(Msg, State) -> [Msg]), {noreply, State}. -delayed_restart(RestartType, Reason, Child, State) -> - case do_restart(RestartType, Reason, Child, State) of - {ok, NState} -> {noreply, NState}; - {shutdown, State2} -> {stop, shutdown, State2} - end. - %% %% Terminate this server. %% @@ -853,6 +837,12 @@ restart_child(Pid, Reason, State) -> {ok, State} end. +try_restart(RestartType, Reason, Child, State) -> + case do_restart(RestartType, Reason, Child, State) of + {ok, NState} -> {noreply, NState}; + {shutdown, State2} -> {stop, shutdown, State2} + end. + do_restart(RestartType, Reason, Child, State) -> maybe_report_error(RestartType, Reason, Child, State), handle_restart(RestartType, Reason, Child, State). -- cgit v1.2.1 From 77146eae65999b23816458bfa9a427d22791eceb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Apr 2013 12:02:21 +0100 Subject: Don't wipe the slate clean each time we get a conserve_resources message. --- src/rabbit_reader.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 9a851101..ec497410 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -324,11 +324,13 @@ mainloop(Deb, State = #v1{sock = Sock, buf = Buf, buf_len = BufLen}) -> end. handle_other({conserve_resources, Source, Conserve}, - State = #v1{throttle = Throttle}) -> - Throttle1 = Throttle#throttle{conserve_resources = case Conserve of - true -> [Source]; - false -> [] - end}, + State = #v1{throttle = Throttle = + #throttle{conserve_resources = CR}}) -> + CR1 = case Conserve of + true -> [Source | CR]; + false -> CR -- [Source] + end, + Throttle1 = Throttle#throttle{conserve_resources = CR1}, control_throttle(State#v1{throttle = Throttle1}); handle_other({channel_closing, ChPid}, State) -> ok = rabbit_channel:ready_for_close(ChPid), -- cgit v1.2.1 From d9610f82621d5e6ad4617fe9fa44ed3887237cea Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 16 Apr 2013 15:27:06 +0100 Subject: relocate is_explicit_restart macro --- src/supervisor2.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index a181e7d4..4014f9ee 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -141,9 +141,6 @@ -define(SETS, sets). -define(SET, set). --define(is_explicit_restart(R), - R == {shutdown, restart}). - -ifdef(use_specs). -record(state, {name, strategy :: strategy(), @@ -172,6 +169,8 @@ (is_tuple(R) andalso tuple_size(R) == 2 andalso element(1, R) =:= permanent))). +-define(is_explicit_restart(R), + R == {shutdown, restart}). -ifdef(use_specs). -callback init(Args :: term()) -> -- cgit v1.2.1 From 5b89f9f11418f0f5725494b88ed852540a7dbcbd Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 16 Apr 2013 15:28:32 +0100 Subject: drop function guards from restart_if_explicit_or_abnormal --- src/supervisor2.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 4014f9ee..8577a06e 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -891,12 +891,9 @@ handle_delayed_restart({intrinsic, _Delay}=Restart, Reason, Child, State) -> Reason, Child, State). restart_if_explicit_or_abnormal(RestartHow, _Otherwise, Reason, Child, State) - when is_function(RestartHow, 2) andalso - ?is_explicit_restart(Reason) -> + when ?is_explicit_restart(Reason) -> RestartHow(Child, State); -restart_if_explicit_or_abnormal(RestartHow, Otherwise, Reason, Child, State) - when is_function(RestartHow, 2) andalso - is_function(Otherwise, 2) -> +restart_if_explicit_or_abnormal(RestartHow, Otherwise, Reason, Child, State) -> case is_abnormal_termination(Reason) of true -> RestartHow(Child, State); false -> Otherwise(Child, State) -- cgit v1.2.1 From 3af08702100e0a7ca293798f5af591c46a03e7d2 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 16 Apr 2013 15:32:24 +0100 Subject: simplify restart_if_explicit_or_abnormal --- src/supervisor2.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 8577a06e..2f2e6692 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -890,11 +890,8 @@ handle_delayed_restart({intrinsic, _Delay}=Restart, Reason, Child, State) -> fun delete_child_and_stop/2, Reason, Child, State). -restart_if_explicit_or_abnormal(RestartHow, _Otherwise, Reason, Child, State) - when ?is_explicit_restart(Reason) -> - RestartHow(Child, State); restart_if_explicit_or_abnormal(RestartHow, Otherwise, Reason, Child, State) -> - case is_abnormal_termination(Reason) of + case ?is_explicit_restart(Reason) orelse is_abnormal_termination(Reason) of true -> RestartHow(Child, State); false -> Otherwise(Child, State) end. -- cgit v1.2.1 From 0c0b55cbb63bdb3b3f3a6604c8aa5690ef23222c Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 16 Apr 2013 15:38:02 +0100 Subject: rationalise restart handling --- src/supervisor2.erl | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 2f2e6692..533ec997 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -871,21 +871,13 @@ handle_restart(intrinsic, Reason, Child, State) -> Reason, Child, State); handle_restart(temporary, _Reason, Child, State) -> delete_child_and_continue(Child, State); -handle_restart({_RestartType, _Delay}=Restart, Reason, Child, State) -> - handle_delayed_restart(Restart, Reason, Child, State). - -handle_delayed_restart({permanent, _Delay}=Restart, Reason, Child, State) -> - do_restart_delay(Restart, Reason, Child, State); -handle_delayed_restart({RestartType, _Delay}=Restart, Reason, Child, State) - when ?is_explicit_restart(Reason) andalso - (RestartType =:= transient orelse - RestartType =:= intrinsic) -> +handle_restart({permanent, _Delay}=Restart, Reason, Child, State) -> do_restart_delay(Restart, Reason, Child, State); -handle_delayed_restart({transient, _Delay}=Restart, Reason, Child, State) -> +handle_restart({transient, _Delay}=Restart, Reason, Child, State) -> restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason), fun delete_child_and_continue/2, Reason, Child, State); -handle_delayed_restart({intrinsic, _Delay}=Restart, Reason, Child, State) -> +handle_restart({intrinsic, _Delay}=Restart, Reason, Child, State) -> restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason), fun delete_child_and_stop/2, Reason, Child, State). -- cgit v1.2.1 From e0d2c7e2c7a2010dda1352bbf9482d7b1ee1bc71 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Apr 2013 16:29:28 +0100 Subject: We can end up hearing about a reason we have already heard about, because rabbit_alarm *both* returns them from register() and immediately calls the MFA during register(). Tests now pass. --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ec497410..a2727067 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -327,7 +327,7 @@ handle_other({conserve_resources, Source, Conserve}, State = #v1{throttle = Throttle = #throttle{conserve_resources = CR}}) -> CR1 = case Conserve of - true -> [Source | CR]; + true -> lists:usort([Source | CR]); false -> CR -- [Source] end, Throttle1 = Throttle#throttle{conserve_resources = CR1}, -- cgit v1.2.1 From 6dff79458ef7b54fe9c253414676a44cd323802e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 19 Apr 2013 12:36:55 +0100 Subject: Refactor restart handling Avoid logging restart (failures) twice. Do not reset {restarting, Pid} to 'undefined' as this deadlocks the supervisor. --- src/supervisor2.erl | 5 +++- src/supervisor2_tests.erl | 58 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 533ec997..ca219990 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -837,7 +837,7 @@ restart_child(Pid, Reason, State) -> end. try_restart(RestartType, Reason, Child, State) -> - case do_restart(RestartType, Reason, Child, State) of + case handle_restart(RestartType, Reason, Child, State) of {ok, NState} -> {noreply, NState}; {shutdown, State2} -> {stop, shutdown, State2} end. @@ -1260,6 +1260,9 @@ state_del_child(Child, State) -> del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary -> Chs; +del_child(NameOrPid, [Ch=#child{pid = ?restarting(_)}|_]=Chs) + when Ch#child.name =:= NameOrPid -> + Chs; del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name -> [Ch#child{pid = undefined} | Chs]; del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary -> diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 518f11b7..c53b613c 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -17,12 +17,22 @@ -module(supervisor2_tests). -behaviour(supervisor2). +-include_lib("eunit/include/eunit.hrl"). + +-define(ASSERT, true). +-define(EUNIT_NOAUTO, true). + -export([test_all/0, start_link/0]). +-export([start_link_bad/0]). -export([init/1]). test_all() -> - ok = check_shutdown(stop, 200, 200, 2000), - ok = check_shutdown(ignored, 1, 2, 2000). + catch ets:new(?MODULE, [named_table, public]), + %% ok = check_shutdown(stop, 200, 200, 2000), + %% ok = check_shutdown(ignored, 1, 2, 2000), + %% ok = check_logging(transient), + ets:delete(?MODULE, bang), + ok = check_logging({permanent, 1}). check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) -> {ok, Sup} = supervisor2:start_link(?MODULE, [SupTimeout]), @@ -52,6 +62,20 @@ check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) -> exit(Sup, shutdown), Res. +check_logging(How) -> + process_flag(trap_exit, true), + {ok, Sup} = supervisor2:start_link(?MODULE, [bang, How]), + io:format("super pid = ~p~n", [Sup]), + MRef = erlang:monitor(process, Sup), + [Pid] = supervisor2:find_child(Sup, test_try_again_sup), + io:format("Pid == ~p~nChildren == ~p~n", [Pid, supervisor2:which_children(Sup)]), + Pid ! {shutdown, bang}, + io:format("restart issued - awaiting sup death~n"), + receive + {'DOWN', MRef, process, Sup, Reason} -> + io:format("stopped Sup == ~p~n", [Reason]) + end. + start_link() -> Pid = spawn_link(fun () -> process_flag(trap_exit, true), @@ -59,6 +83,35 @@ start_link() -> end), {ok, Pid}. +start_link_bad() -> + Boom = ets:lookup(?MODULE, bang), + case Boom of + [{bang, true}] -> io:format("BOOM!~n"), exit(bang); + _ -> ok + end, + io:format("no Boom - starting server~n"), + Pid = spawn_link(fun () -> + process_flag(trap_exit, true), + receive + {shutdown, Bang} -> + ets:insert(?MODULE, [{bang, true}]), + io:format("exiting...~n"), + exit(Bang); + shutdown -> + io:format("exiting (shutdown)...~n"), + exit(shutdown); + Other -> + io:format("odd signal: ~p~n", [Other]), + exit(Other) + end + end), + {ok, Pid}. + +init([bang, How]) -> + {ok, {{one_for_one, 3, 10}, + [{test_try_again_sup, {?MODULE, start_link_bad, []}, + How, 5000, worker, [?MODULE]}]}}; + init([Timeout]) -> {ok, {{one_for_one, 0, 1}, [{test_sup, {supervisor2, start_link, @@ -68,3 +121,4 @@ init([]) -> {ok, {{simple_one_for_one, 0, 1}, [{test_worker, {?MODULE, start_link, []}, temporary, 1000, worker, [?MODULE]}]}}. + -- cgit v1.2.1 From 1b52a408c67351ceb4ae7b6bb3890885520681d9 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 19 Apr 2013 13:46:38 +0100 Subject: back out of f608b9df9a14 changes supervisor2_tests --- src/supervisor2_tests.erl | 58 ++--------------------------------------------- 1 file changed, 2 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index c53b613c..518f11b7 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -17,22 +17,12 @@ -module(supervisor2_tests). -behaviour(supervisor2). --include_lib("eunit/include/eunit.hrl"). - --define(ASSERT, true). --define(EUNIT_NOAUTO, true). - -export([test_all/0, start_link/0]). --export([start_link_bad/0]). -export([init/1]). test_all() -> - catch ets:new(?MODULE, [named_table, public]), - %% ok = check_shutdown(stop, 200, 200, 2000), - %% ok = check_shutdown(ignored, 1, 2, 2000), - %% ok = check_logging(transient), - ets:delete(?MODULE, bang), - ok = check_logging({permanent, 1}). + ok = check_shutdown(stop, 200, 200, 2000), + ok = check_shutdown(ignored, 1, 2, 2000). check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) -> {ok, Sup} = supervisor2:start_link(?MODULE, [SupTimeout]), @@ -62,20 +52,6 @@ check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) -> exit(Sup, shutdown), Res. -check_logging(How) -> - process_flag(trap_exit, true), - {ok, Sup} = supervisor2:start_link(?MODULE, [bang, How]), - io:format("super pid = ~p~n", [Sup]), - MRef = erlang:monitor(process, Sup), - [Pid] = supervisor2:find_child(Sup, test_try_again_sup), - io:format("Pid == ~p~nChildren == ~p~n", [Pid, supervisor2:which_children(Sup)]), - Pid ! {shutdown, bang}, - io:format("restart issued - awaiting sup death~n"), - receive - {'DOWN', MRef, process, Sup, Reason} -> - io:format("stopped Sup == ~p~n", [Reason]) - end. - start_link() -> Pid = spawn_link(fun () -> process_flag(trap_exit, true), @@ -83,35 +59,6 @@ start_link() -> end), {ok, Pid}. -start_link_bad() -> - Boom = ets:lookup(?MODULE, bang), - case Boom of - [{bang, true}] -> io:format("BOOM!~n"), exit(bang); - _ -> ok - end, - io:format("no Boom - starting server~n"), - Pid = spawn_link(fun () -> - process_flag(trap_exit, true), - receive - {shutdown, Bang} -> - ets:insert(?MODULE, [{bang, true}]), - io:format("exiting...~n"), - exit(Bang); - shutdown -> - io:format("exiting (shutdown)...~n"), - exit(shutdown); - Other -> - io:format("odd signal: ~p~n", [Other]), - exit(Other) - end - end), - {ok, Pid}. - -init([bang, How]) -> - {ok, {{one_for_one, 3, 10}, - [{test_try_again_sup, {?MODULE, start_link_bad, []}, - How, 5000, worker, [?MODULE]}]}}; - init([Timeout]) -> {ok, {{one_for_one, 0, 1}, [{test_sup, {supervisor2, start_link, @@ -121,4 +68,3 @@ init([]) -> {ok, {{simple_one_for_one, 0, 1}, [{test_worker, {?MODULE, start_link, []}, temporary, 1000, worker, [?MODULE]}]}}. - -- cgit v1.2.1 From 7475a7a9c18fe421c4c69f80903b5d8986c02741 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 26 Apr 2013 12:39:40 +0100 Subject: Minor variable renaming, document subtle one_for_all child deletion clauses --- src/supervisor2.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index ca219990..3b971739 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -1260,11 +1260,12 @@ state_del_child(Child, State) -> del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary -> Chs; -del_child(NameOrPid, [Ch=#child{pid = ?restarting(_)}|_]=Chs) - when Ch#child.name =:= NameOrPid -> +del_child(Name, [Ch=#child{pid = ?restarting(_)}|_]=Chs) + when Ch#child.name =:= Name -> Chs; del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name -> [Ch#child{pid = undefined} | Chs]; +%% the next two clauses only handle deletions during a one_for_all restart del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary -> Chs; del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid -> -- cgit v1.2.1 From c44afbbda908628527caf1dba399b49bf0653b43 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 1 May 2013 10:59:16 +0100 Subject: ever so slightly clearer --- src/supervisor2.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 3b971739..8b0899a6 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -1258,14 +1258,12 @@ state_del_child(Child, State) -> NChildren = del_child(Child#child.name, State#state.children), State#state{children = NChildren}. -del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary -> +del_child(Name, [Ch=#child{pid = ?restarting(_)}|_]=Chs) when Ch#child.name =:= Name -> Chs; -del_child(Name, [Ch=#child{pid = ?restarting(_)}|_]=Chs) - when Ch#child.name =:= Name -> +del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary -> Chs; del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name -> [Ch#child{pid = undefined} | Chs]; -%% the next two clauses only handle deletions during a one_for_all restart del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary -> Chs; del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid -> -- cgit v1.2.1 From 5b3fccb6dcec837fc7e00a0fbee0a9f72f07eaa4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 7 May 2013 07:52:19 +0100 Subject: remove R13-ism imported from upstream --- src/supervisor2.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 8b0899a6..a51bc15c 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -655,8 +655,9 @@ handle_cast({try_again_restart,Pid,Reason}, #state{children=[Child]}=State) end; handle_cast({try_again_restart,Name,Reason}, State) -> - case lists:keyfind(Name,#child.name,State#state.children) of - Child = #child{pid=?restarting(_), restart_type=RestartType} -> + %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist + case lists:keysearch(Name,#child.name,State#state.children) of + {value, Child = #child{pid=?restarting(_), restart_type=RestartType}} -> try_restart(RestartType, Reason, Child, State); _ -> {noreply,State} -- cgit v1.2.1 From a452b5d2b0fc0bf22a5b35d3df7abfe4de32afea Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 14 May 2013 13:10:37 +0100 Subject: Cope with trailing zeroes in journal and segment files Makes the on-disk journal format tolerant of runs of zeroes at the end of the file. This could occur if file operations are interrupted by a crashing server. Also changes the definition of segment prefixes so that runs of zero bytes will never appear in a valid segment. --- src/rabbit_queue_index.erl | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index ea70208f..230639ee 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -23,7 +23,7 @@ -export([scan/3]). --export([add_queue_ttl/0]). +-export([add_queue_ttl/0, avoid_zeroes/0]). -define(CLEAN_FILENAME, "clean.dot"). @@ -125,7 +125,7 @@ %% seq only is binary 00 followed by 14 bits of rel seq id %% (range: 0 - 16383) --define(REL_SEQ_ONLY_PREFIX, 00). +-define(REL_SEQ_ONLY_PREFIX, 01). -define(REL_SEQ_ONLY_PREFIX_BITS, 2). -define(REL_SEQ_ONLY_RECORD_BYTES, 2). @@ -171,6 +171,7 @@ %%---------------------------------------------------------------------------- -rabbit_upgrade({add_queue_ttl, local, []}). +-rabbit_upgrade({avoid_zeroes, local, [add_queue_ttl]}). -ifdef(use_specs). @@ -715,7 +716,12 @@ load_journal_entries(State = #qistate { journal_handle = Hdl }) -> load_journal_entries(add_to_journal(SeqId, ack, State)); _ -> case file_handle_cache:read(Hdl, ?PUB_RECORD_BODY_BYTES) of - {ok, Bin} -> + %% Journal entry composed only of zeroes was probably + %% produced during a dirty shutdown so stop reading + {ok, <<0:?PUB_RECORD_BODY_BYTES/unit:8>>} + when Prefix =:= ?PUB_PERSIST_JPREFIX -> + State; + {ok, <>} -> {MsgId, MsgProps} = parse_pub_record_body(Bin), IsPersistent = case Prefix of ?PUB_PERSIST_JPREFIX -> true; @@ -1057,6 +1063,21 @@ add_queue_ttl_segment(< stop. +avoid_zeroes() -> + foreach_queue_index({none, fun avoid_zeroes_segment/1}). + +avoid_zeroes_segment(<>) -> + {<>, Rest}; +avoid_zeroes_segment(<<0:?REL_SEQ_ONLY_PREFIX_BITS, + RelSeq:?REL_SEQ_BITS, Rest/binary>>) -> + {<>, + Rest}; +avoid_zeroes_segment(_) -> + stop. + %%---------------------------------------------------------------------------- foreach_queue_index(Funs) -> @@ -1081,7 +1102,9 @@ transform_queue(Dir, Gatherer, {JournalFun, SegmentFun}) -> || Seg <- rabbit_file:wildcard(".*\\" ++ ?SEGMENT_EXTENSION, Dir)], ok = gatherer:finish(Gatherer). -transform_file(Path, Fun) -> +transform_file(_Path, none) -> + ok; +transform_file(Path, Fun) when is_function(Fun)-> PathTmp = Path ++ ".upgrade", case rabbit_file:file_size(Path) of 0 -> ok; -- cgit v1.2.1 From ad1f0620e302bf62ae30fa24cac79eb079101fbc Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 14 May 2013 14:31:42 +0100 Subject: Implement feedback --- src/rabbit_queue_index.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 230639ee..847d39c1 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -123,7 +123,7 @@ -define(REL_SEQ_BITS, 14). -define(SEGMENT_ENTRY_COUNT, 16384). %% trunc(math:pow(2,?REL_SEQ_BITS))). -%% seq only is binary 00 followed by 14 bits of rel seq id +%% seq only is binary 01 followed by 14 bits of rel seq id %% (range: 0 - 16383) -define(REL_SEQ_ONLY_PREFIX, 01). -define(REL_SEQ_ONLY_PREFIX_BITS, 2). @@ -718,10 +718,9 @@ load_journal_entries(State = #qistate { journal_handle = Hdl }) -> case file_handle_cache:read(Hdl, ?PUB_RECORD_BODY_BYTES) of %% Journal entry composed only of zeroes was probably %% produced during a dirty shutdown so stop reading - {ok, <<0:?PUB_RECORD_BODY_BYTES/unit:8>>} - when Prefix =:= ?PUB_PERSIST_JPREFIX -> + {ok, <<0:?PUB_RECORD_BODY_BYTES/unit:8>>} -> State; - {ok, <>} -> + {ok, <>} -> {MsgId, MsgProps} = parse_pub_record_body(Bin), IsPersistent = case Prefix of ?PUB_PERSIST_JPREFIX -> true; -- cgit v1.2.1 From 973200ef9f6bf82b73b523dba8d7b17911466f6a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 15 May 2013 17:50:25 +0100 Subject: Quick and dirty hooks for federated queues. --- src/rabbit_amqqueue_process.erl | 11 +++++++++-- src/rabbit_channel.erl | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d2f4a178..6eb81869 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -190,6 +190,10 @@ declare(Recover, From, State = #q{q = Q, recovery_barrier(Recover), State1 = process_args(State#q{backing_queue = BQ, backing_queue_state = BQS}), + case Q#amqqueue.name#resource.name of + <<"test">> -> rabbit_federation_queue:start_link(Q); + _ -> ok + end, rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State1)), rabbit_event:if_enabled(State1, #q.stats_timer, @@ -518,11 +522,14 @@ discard(#delivery{sender = SenderPid, end, BQS1 = BQ:discard(MsgId, SenderPid, BQS), State1#q{backing_queue_state = BQS1}. - -run_message_queue(State) -> +run_message_queue(State = #q{q = Q}) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, is_empty(State), State), + case queue:len(State1#q.active_consumers) of + 0 -> rabbit_federation_queue:stop(Q); + _ -> rabbit_federation_queue:go(Q) + end, State1. attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 37041d34..69836204 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1219,8 +1219,8 @@ parse_credit_args(Arguments) -> case rabbit_misc:table_lookup(Arguments, <<"x-credit">>) of {table, T} -> case {rabbit_misc:table_lookup(T, <<"credit">>), rabbit_misc:table_lookup(T, <<"drain">>)} of - {{long, Credit}, {boolean, Drain}} -> {Credit, Drain}; - _ -> none + {{long, Credit}, {bool, Drain}} -> {Credit, Drain}; + _ -> none end; undefined -> none end. -- cgit v1.2.1 From 9c83a7408fad5844e1e0f5aa9e9397978bc77125 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 21 May 2013 12:13:17 +0100 Subject: This may be the last Mnesia restart before we boot, so let's make sure the tables are there. --- src/rabbit_upgrade.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index fde0dbe1..cffd6ae0 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -247,6 +247,7 @@ apply_upgrades(Scope, Upgrades, Fun) -> ok = rabbit_file:lock_file(lock_filename()), info("~s upgrades: ~w to apply~n", [Scope, length(Upgrades)]), rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), + rabbit_table:wait_for_replicated(), Fun(), [apply_upgrade(Scope, Upgrade) || Upgrade <- Upgrades], info("~s upgrades: All upgrades applied successfully~n", [Scope]), -- cgit v1.2.1 From d7f3b76387a93c466b2495cc1fcf84f7c62f3d8d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 21 May 2013 12:24:22 +0100 Subject: Only wait for tables once local upgrades are over, not mnesia ones. --- src/rabbit_upgrade.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index cffd6ae0..b9f25ef1 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -238,6 +238,7 @@ maybe_upgrade_local() -> ok = apply_upgrades(local, Upgrades, fun () -> ok end), ensure_backup_removed(), + rabbit_table:wait_for_replicated(), ok end. @@ -247,7 +248,6 @@ apply_upgrades(Scope, Upgrades, Fun) -> ok = rabbit_file:lock_file(lock_filename()), info("~s upgrades: ~w to apply~n", [Scope, length(Upgrades)]), rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), - rabbit_table:wait_for_replicated(), Fun(), [apply_upgrade(Scope, Upgrade) || Upgrade <- Upgrades], info("~s upgrades: All upgrades applied successfully~n", [Scope]), -- cgit v1.2.1 From d7002da58f3ab9eed44a453123889c124b3d305e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 21 May 2013 13:17:57 +0100 Subject: No, that broke upgrades on RAM nodes. Third time's the charm... --- src/rabbit_mnesia.erl | 5 +++-- src/rabbit_upgrade.erl | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 8cd976fa..6ea91086 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -465,10 +465,11 @@ init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes) -> %% about the cluster case NodeType of ram -> start_mnesia(), - change_extra_db_nodes(ClusterNodes, false), - rabbit_table:wait_for_replicated(); + change_extra_db_nodes(ClusterNodes, false); disc -> ok end, + %% ...and all nodes will need to wait for tables + rabbit_table:wait_for_replicated(), ok. init_db_with_mnesia(ClusterNodes, NodeType, diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index b9f25ef1..fde0dbe1 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -238,7 +238,6 @@ maybe_upgrade_local() -> ok = apply_upgrades(local, Upgrades, fun () -> ok end), ensure_backup_removed(), - rabbit_table:wait_for_replicated(), ok end. -- cgit v1.2.1 From e4f2fa497a7b209d67bf9cc7208f1f48a997cadf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 22 May 2013 13:04:38 +0100 Subject: Only consume if we are empty. --- src/rabbit_amqqueue_process.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6eb81869..0fda5b8e 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -522,13 +522,14 @@ discard(#delivery{sender = SenderPid, end, BQS1 = BQ:discard(MsgId, SenderPid, BQS), State1#q{backing_queue_state = BQS1}. + run_message_queue(State = #q{q = Q}) -> - {_IsEmpty1, State1} = deliver_msgs_to_consumers( + {IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, is_empty(State), State), - case queue:len(State1#q.active_consumers) of - 0 -> rabbit_federation_queue:stop(Q); - _ -> rabbit_federation_queue:go(Q) + case queue:len(State1#q.active_consumers) =/= 0 andalso IsEmpty1 of + true -> rabbit_federation_queue:go(Q); + false -> rabbit_federation_queue:stop(Q) end, State1. -- cgit v1.2.1 From 9009c8a625731314a29ab031c9a3a53e4d9e177c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 23 May 2013 12:53:08 +0100 Subject: Distinguish between federated consumers and others. --- src/rabbit_amqqueue.erl | 11 ++++++----- src/rabbit_amqqueue_process.erl | 19 +++++++++++++++---- src/rabbit_channel.erl | 17 +++++++++-------- 3 files changed, 30 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8c00c85c..f6935f25 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -26,7 +26,7 @@ -export([list/0, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). -export([force_event_refresh/0, wake_up/1]). -export([consumers/1, consumers_all/1, consumer_info_keys/0]). --export([basic_get/4, basic_consume/9, basic_cancel/4]). +-export([basic_get/4, basic_consume/10, basic_cancel/4]). -export([notify_sent/2, notify_sent_queue_down/1, resume/2, flush_all/2]). -export([notify_down_all/2, activate_limit_all/2, credit/5]). -export([on_node_down/1]). @@ -149,9 +149,9 @@ {'ok', non_neg_integer(), qmsg()} | 'empty'). -spec(credit/5 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), non_neg_integer(), boolean()) -> 'ok'). --spec(basic_consume/9 :: +-spec(basic_consume/10 :: (rabbit_types:amqqueue(), boolean(), pid(), pid(), boolean(), - rabbit_types:ctag(), boolean(), {non_neg_integer(), boolean()} | 'none', any()) + rabbit_types:ctag(), boolean(), {non_neg_integer(), boolean()} | 'none', any(), any()) -> rabbit_types:ok_or_error('exclusive_consume_unavailable')). -spec(basic_cancel/4 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok'). @@ -549,9 +549,10 @@ basic_get(#amqqueue{pid = QPid}, ChPid, NoAck, LimiterPid) -> delegate:call(QPid, {basic_get, ChPid, NoAck, LimiterPid}). basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, LimiterPid, LimiterActive, - ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg) -> + ConsumerTag, ExclusiveConsume, CreditArgs, OtherArgs, OkMsg) -> delegate:call(QPid, {basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, - ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg}). + ConsumerTag, ExclusiveConsume, CreditArgs, OtherArgs, + OkMsg}). basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) -> delegate:call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0fda5b8e..6b6bfa92 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -55,7 +55,7 @@ status }). --record(consumer, {tag, ack_required}). +-record(consumer, {tag, ack_required, args}). %% These are held in our process dictionary -record(cr, {ch_pid, @@ -527,12 +527,22 @@ run_message_queue(State = #q{q = Q}) -> {IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, is_empty(State), State), - case queue:len(State1#q.active_consumers) =/= 0 andalso IsEmpty1 of + case IsEmpty1 andalso active_unfederated(State1#q.active_consumers) of true -> rabbit_federation_queue:go(Q); false -> rabbit_federation_queue:stop(Q) end, State1. +active_unfederated(Cs) -> + case queue:out(Cs) of + {empty, _} -> false; + {{value, {_Pid, #consumer{args = Args}}}, Cs1} -> + case rabbit_misc:table_lookup(Args, <<"x-purpose">>) of + {longstr, <<"federation">>} -> active_unfederated(Cs1); + _ -> true + end + end. + attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, Props, Delivered, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -1118,7 +1128,7 @@ handle_call({basic_get, ChPid, NoAck, LimiterPid}, _From, end; handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, - ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg}, + ConsumerTag, ExclusiveConsume, CreditArgs, OtherArgs, OkMsg}, _From, State = #q{exclusive_consumer = Holder}) -> case check_exclusive_access(Holder, ExclusiveConsume, State) of in_use -> @@ -1142,7 +1152,8 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, false -> ok end, Consumer = #consumer{tag = ConsumerTag, - ack_required = not NoAck}, + ack_required = not NoAck, + args = OtherArgs}, ExclusiveConsumer = if ExclusiveConsume -> {ChPid, ConsumerTag}; true -> Holder end, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 69836204..a281f7cb 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -734,7 +734,7 @@ handle_method(#'basic.consume'{queue = QueueNameBin, no_ack = NoAck, exclusive = ExclusiveConsume, nowait = NoWait, - arguments = Arguments}, + arguments = Args}, _, State = #ch{conn_pid = ConnPid, limiter = Limiter, consumer_mapping = ConsumerMapping}) -> @@ -755,12 +755,13 @@ handle_method(#'basic.consume'{queue = QueueNameBin, case rabbit_amqqueue:with_exclusive_access_or_die( QueueName, ConnPid, fun (Q) -> + {CreditArgs, OtherArgs} = parse_credit_args(Args), {rabbit_amqqueue:basic_consume( Q, NoAck, self(), rabbit_limiter:pid(Limiter), rabbit_limiter:is_active(Limiter), ActualConsumerTag, ExclusiveConsume, - parse_credit_args(Arguments), + CreditArgs, OtherArgs, ok_msg(NoWait, #'basic.consume_ok'{ consumer_tag = ActualConsumerTag})), Q} @@ -1217,12 +1218,12 @@ handle_delivering_queue_down(QPid, State = #ch{delivering_queues = DQ}) -> parse_credit_args(Arguments) -> case rabbit_misc:table_lookup(Arguments, <<"x-credit">>) of - {table, T} -> case {rabbit_misc:table_lookup(T, <<"credit">>), - rabbit_misc:table_lookup(T, <<"drain">>)} of - {{long, Credit}, {bool, Drain}} -> {Credit, Drain}; - _ -> none - end; - undefined -> none + {table, T} -> {case {rabbit_misc:table_lookup(T, <<"credit">>), + rabbit_misc:table_lookup(T, <<"drain">>)} of + {{long, Credit}, {bool, Drain}} -> {Credit, Drain}; + _ -> none + end, lists:keydelete(<<"x-credit">>, 1, Arguments)}; + undefined -> {none, Arguments} end. binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin, -- cgit v1.2.1 From b3335f9b6f64f8b6e36a6ffa5525baa62906c7be Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 23 May 2013 14:33:58 +0100 Subject: Rename this --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6b6bfa92..6ebfd1f3 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -528,7 +528,7 @@ run_message_queue(State = #q{q = Q}) -> fun deliver_from_queue_deliver/2, is_empty(State), State), case IsEmpty1 andalso active_unfederated(State1#q.active_consumers) of - true -> rabbit_federation_queue:go(Q); + true -> rabbit_federation_queue:run(Q); false -> rabbit_federation_queue:stop(Q) end, State1. -- cgit v1.2.1 From 2a245a183664fb656c54ce3f22e655dc790186d3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 23 May 2013 17:46:11 +0100 Subject: Tell the federated queue about basic.get. --- src/rabbit_amqqueue_process.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6ebfd1f3..2dc85d63 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1107,11 +1107,12 @@ handle_call({notify_down, ChPid}, _From, State) -> end; handle_call({basic_get, ChPid, NoAck, LimiterPid}, _From, - State = #q{q = #amqqueue{name = QName}}) -> + State = #q{q = Q = #amqqueue{name = QName}}) -> AckRequired = not NoAck, State1 = ensure_expiry_timer(State), case fetch(AckRequired, State1) of {empty, State2} -> + rabbit_federation_queue:basic_get(Q), reply(empty, State2); {{Message, IsDelivered, AckTag}, State2} -> State3 = #q{backing_queue = BQ, backing_queue_state = BQS} = -- cgit v1.2.1 From 89b3be6ef4138452869b82324c582a1aff72c6c8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 24 May 2013 11:37:15 +0100 Subject: Make sure we update the federation state when a consumer becomes inactive or is cancelled. --- src/rabbit_amqqueue_process.erl | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2dc85d63..71dbb9de 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -440,12 +440,14 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> C = lookup_ch(ChPid), case is_ch_blocked(C) of true -> block_consumer(C, E), + notify_federation(State), {false, State}; false -> case rabbit_limiter:can_send(C#cr.limiter, Consumer#consumer.ack_required, Consumer#consumer.tag) of {suspend, Limiter} -> block_consumer(C#cr{limiter = Limiter}, E), + notify_federation(State), {false, State}; {continue, Limiter} -> AC1 = queue:in(E, State#q.active_consumers), @@ -523,15 +525,22 @@ discard(#delivery{sender = SenderPid, BQS1 = BQ:discard(MsgId, SenderPid, BQS), State1#q{backing_queue_state = BQS1}. -run_message_queue(State = #q{q = Q}) -> - {IsEmpty1, State1} = deliver_msgs_to_consumers( +run_message_queue(State) -> + {_IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, is_empty(State), State), - case IsEmpty1 andalso active_unfederated(State1#q.active_consumers) of + notify_federation(State1), + State1. + +notify_federation(#q{q = Q, + active_consumers = ActiveConsumers, + backing_queue = BQ, + backing_queue_state = BQS}) -> + IsEmpty = BQ:is_empty(BQS), + case IsEmpty andalso active_unfederated(ActiveConsumers) of true -> rabbit_federation_queue:run(Q); false -> rabbit_federation_queue:stop(Q) - end, - State1. + end. active_unfederated(Cs) -> case queue:out(Cs) of @@ -1194,6 +1203,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, _From, active_consumers = remove_consumer( ChPid, ConsumerTag, State#q.active_consumers)}, + notify_federation(State1), case should_auto_delete(State1) of false -> reply(ok, ensure_expiry_timer(State1)); true -> stop(ok, State1) -- cgit v1.2.1 From 7b79de595d6b6fb5db1478072b81c22e5723b999 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 24 May 2013 14:12:09 +0100 Subject: If we have local active consumers, ignore federated ones. --- src/rabbit_amqqueue_process.erl | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 71dbb9de..4c3751ba 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -426,7 +426,7 @@ deliver_msgs_to_consumers(_DeliverFun, true, State) -> {true, State}; deliver_msgs_to_consumers(DeliverFun, false, State = #q{active_consumers = ActiveConsumers}) -> - case queue:out(ActiveConsumers) of + case pick_consumer(ActiveConsumers) of {empty, _} -> {false, State}; {{value, QEntry}, Tail} -> @@ -544,12 +544,25 @@ notify_federation(#q{q = Q, active_unfederated(Cs) -> case queue:out(Cs) of - {empty, _} -> false; - {{value, {_Pid, #consumer{args = Args}}}, Cs1} -> - case rabbit_misc:table_lookup(Args, <<"x-purpose">>) of - {longstr, <<"federation">>} -> active_unfederated(Cs1); - _ -> true - end + {empty, _} -> false; + {{value, C}, Cs1} -> case federated_consumer(C) of + true -> active_unfederated(Cs1); + false -> true + end + end. + +%% TODO this could be more efficient. But we'd need another representation, +%% and thus to abstract the representation of active_consumers. +pick_consumer(Cs) -> + case lists:splitwith(fun federated_consumer/1, queue:to_list(Cs)) of + {_, []} -> queue:out(Cs); + {Feds, [UnFed|Tl]} -> queue:out(queue:from_list([UnFed | Feds ++ Tl])) + end. + +federated_consumer({_Pid, #consumer{args = Args}}) -> + case rabbit_misc:table_lookup(Args, <<"x-purpose">>) of + {longstr, <<"federation">>} -> true; + _ -> false end. attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, -- cgit v1.2.1 From d4c1dd230e08fe9794a4508c52030d1c6290e73a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 25 May 2013 21:57:33 +0100 Subject: simplify error logging --- src/rabbit_access_control.erl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 16387268..5b92a5c3 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -71,9 +71,7 @@ check_vhost_access(User = #user{ username = Username, rabbit_vhost:exists(VHostPath) andalso Module:check_vhost_access(User, VHostPath) end, - "~s failed checking vhost access to ~s for ~s: ~p~n", - [Module, VHostPath, Username], - "access to vhost '~s' refused for user '~s'", + Module, "access to vhost '~s' refused for user '~s'", [VHostPath, Username]). check_resource_access(User, R = #resource{kind = exchange, name = <<"">>}, @@ -84,15 +82,14 @@ check_resource_access(User = #user{username = Username, auth_backend = Module}, Resource, Permission) -> check_access( fun() -> Module:check_resource_access(User, Resource, Permission) end, - "~s failed checking resource access to ~p for ~s: ~p~n", - [Module, Resource, Username], - "access to ~s refused for user '~s'", + Module, "access to ~s refused for user '~s'", [rabbit_misc:rs(Resource), Username]). -check_access(Fun, ErrStr, ErrArgs, RefStr, RefArgs) -> +check_access(Fun, Module, ErrStr, ErrArgs) -> Allow = case Fun() of - {error, _} = E -> - rabbit_log:error(ErrStr, ErrArgs ++ [E]), + {error, E} -> + rabbit_log:error(ErrStr ++ " by ~s: ~p~n", + ErrArgs ++ [Module, E]), false; Else -> Else @@ -101,5 +98,5 @@ check_access(Fun, ErrStr, ErrArgs, RefStr, RefArgs) -> true -> ok; false -> - rabbit_misc:protocol_error(access_refused, RefStr, RefArgs) + rabbit_misc:protocol_error(access_refused, ErrStr, ErrArgs) end. -- cgit v1.2.1 From 22280a1f1a1c716805a58b75e50d9f6e3df43f74 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 30 May 2013 17:46:37 +0100 Subject: Rewrite a frankly slightly dubious TODO --- src/rabbit_auth_mechanism_plain.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/rabbit_auth_mechanism_plain.erl b/src/rabbit_auth_mechanism_plain.erl index a35a133a..aeea33b8 100644 --- a/src/rabbit_auth_mechanism_plain.erl +++ b/src/rabbit_auth_mechanism_plain.erl @@ -31,9 +31,8 @@ %% SASL PLAIN, as used by the Qpid Java client and our clients. Also, %% apparently, by OpenAMQ. -%% TODO: once the minimum erlang becomes R13B03, reimplement this -%% using the binary module - that makes use of BIFs to do binary -%% matching and will thus be much faster. +%% TODO: reimplement this using the binary module? - that makes use of +%% BIFs to do binary matching and will thus be much faster. description() -> [{description, <<"SASL PLAIN authentication mechanism">>}]. -- cgit v1.2.1 From 06577dd4ae4c5261c9ba544311bcb8723b20a8f1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 30 May 2013 17:47:14 +0100 Subject: Use os:timestamp/0 --- src/rabbit_event.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index a91a9916..3d71b58d 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -141,8 +141,6 @@ notify_if(true, Type, Props) -> notify(Type, Props); notify_if(false, _Type, _Props) -> ok. notify(Type, Props) -> - %% TODO: switch to os:timestamp() when we drop support for - %% Erlang/OTP < R13B01 gen_event:notify(?MODULE, #event{type = Type, props = Props, - timestamp = now()}). + timestamp = os:timestamp()}). -- cgit v1.2.1 From 9fb4137bd1a826a050e64ef910fc0ecd8d963c3a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 30 May 2013 17:47:30 +0100 Subject: Remove mochiweb warning. --- src/rabbit_plugins_main.erl | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'src') diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 308b80cd..10f07f2d 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -121,7 +121,6 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> fmt_missing("dependencies", MissingDeps)}) end, write_enabled_plugins(PluginsFile, NewEnabled), - maybe_warn_mochiweb(NewImplicitlyEnabled), case NewEnabled -- ImplicitlyEnabled of [] -> io:format("Plugin configuration unchanged.~n"); _ -> print_list("The following plugins have been enabled:", @@ -263,25 +262,6 @@ write_enabled_plugins(PluginsFile, Plugins) -> PluginsFile, Reason}}) end. -maybe_warn_mochiweb(Enabled) -> - V = erlang:system_info(otp_release), - case lists:member(mochiweb, Enabled) andalso V < "R13B01" of - true -> - Stars = string:copies("*", 80), - io:format("~n~n~s~n" - " Warning: Mochiweb enabled and Erlang version ~s " - "detected.~n" - " Enabling plugins that depend on Mochiweb is not " - "supported on this Erlang~n" - " version. At least R13B01 is required.~n~n" - " RabbitMQ will not start successfully in this " - "configuration. You *must*~n" - " disable the Mochiweb plugin, or upgrade Erlang.~n" - "~s~n~n~n", [Stars, V, Stars]); - false -> - ok - end. - report_change() -> io:format("Plugin configuration has changed. " "Restart RabbitMQ for changes to take effect.~n"). -- cgit v1.2.1 From a9f9b48512db25c77f006a35251ad484ade3d619 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 1 Jun 2013 22:16:29 +0100 Subject: refactor: replace :F with funs the former is evil --- src/rabbit_direct.erl | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index 53144f3f..c18b5023 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -76,21 +76,23 @@ connect(User = #user{}, VHost, Protocol, Pid, Infos) -> end; connect({Username, Password}, VHost, Protocol, Pid, Infos) -> - connect0(check_user_pass_login, Username, Password, VHost, Protocol, Pid, - Infos); + connect0(fun () -> rabbit_access_control:check_user_pass_login( + Username, Password) end, + VHost, Protocol, Pid, Infos); connect(Username, VHost, Protocol, Pid, Infos) -> - connect0(check_user_login, Username, [], VHost, Protocol, Pid, Infos). + connect0(fun () -> rabbit_access_control:check_user_login( + Username, []) end, + VHost, Protocol, Pid, Infos). -connect0(FunctionName, U, P, VHost, Protocol, Pid, Infos) -> +connect0(AuthFun, VHost, Protocol, Pid, Infos) -> case rabbit:is_running() of - true -> - case rabbit_access_control:FunctionName(U, P) of - {ok, User} -> connect(User, VHost, Protocol, Pid, Infos); - {refused, _M, _A} -> {error, auth_failure} - end; - false -> - {error, broker_not_found_on_node} + true -> case AuthFun() of + {ok, User} -> connect(User, VHost, Protocol, Pid, + Infos); + {refused, _M, _A} -> {error, auth_failure} + end; + false -> {error, broker_not_found_on_node} end. -- cgit v1.2.1 From a0304fe49de7d4afc1974ae4dd302101857536db Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 1 Jun 2013 22:32:14 +0100 Subject: cosmetic(ish): tighter signature for rabbit_direct:connect --- src/rabbit_direct.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index c18b5023..769c86c3 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -35,8 +35,10 @@ {rabbit_types:username(), rabbit_types:password()}), rabbit_types:vhost(), rabbit_types:protocol(), pid(), rabbit_event:event_props()) -> - {'ok', {rabbit_types:user(), - rabbit_framing:amqp_table()}}). + rabbit_types:ok_or_error2( + {rabbit_types:user(), rabbit_framing:amqp_table()}, + 'broker_not_found_on_node' | 'auth_failure' | + 'access_refused')). -spec(start_channel/9 :: (rabbit_channel:channel_number(), pid(), pid(), string(), rabbit_types:protocol(), rabbit_types:user(), rabbit_types:vhost(), -- cgit v1.2.1 From 88e20d03a2c80aeee37df6f50bc610bd2a0ff999 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 1 Jun 2013 22:39:33 +0100 Subject: fix types --- src/priority_queue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 0dc19819..18e1e8d9 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -51,7 +51,7 @@ -type(q() :: pqueue()). -type(priority() :: integer() | 'infinity'). --type(squeue() :: {queue, [any()], [any()]}). +-type(squeue() :: {queue, [any()], [any()], non_neg_integer()}). -type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}). -spec(new/0 :: () -> pqueue()). -- cgit v1.2.1 From 146b12abaed07a00fa48df620440df9ae30d9a6f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 3 Jun 2013 14:55:00 +0100 Subject: Inform fed queues of policy change and termination, and abstract startup a bit more. --- src/rabbit_amqqueue.erl | 1 + src/rabbit_amqqueue_process.erl | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index f6935f25..4d79e096 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -291,6 +291,7 @@ store_queue(Q = #amqqueue{durable = false}) -> policy_changed(Q1, Q2) -> rabbit_mirror_queue_misc:update_mirrors(Q1, Q2), + rabbit_federation_queue:policy_changed(Q1, Q2), %% Make sure we emit a stats event even if nothing %% mirroring-related has changed - the policy may have changed anyway. wake_up(Q1). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4c3751ba..5278c290 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -190,10 +190,7 @@ declare(Recover, From, State = #q{q = Q, recovery_barrier(Recover), State1 = process_args(State#q{backing_queue = BQ, backing_queue_state = BQS}), - case Q#amqqueue.name#resource.name of - <<"test">> -> rabbit_federation_queue:start_link(Q); - _ -> ok - end, + rabbit_federation_queue:maybe_start(Q), rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State1)), rabbit_event:if_enabled(State1, #q.stats_timer, @@ -260,7 +257,7 @@ init_dlx_routing_key(RoutingKey, State) -> init_max_length(MaxLen, State) -> State#q{max_length = MaxLen}. -terminate_shutdown(Fun, State) -> +terminate_shutdown(Fun, State = #q{q = Q}) -> State1 = #q{backing_queue_state = BQS} = lists:foldl(fun (F, S) -> F(S) end, State, [fun stop_sync_timer/1, @@ -271,6 +268,7 @@ terminate_shutdown(Fun, State) -> undefined -> State1; _ -> ok = rabbit_memory_monitor:deregister(self()), QName = qname(State), + rabbit_federation_queue:terminate(Q), [emit_consumer_deleted(Ch, CTag, QName) || {Ch, CTag, _} <- consumers(State1)], State1#q{backing_queue_state = Fun(BQS)} -- cgit v1.2.1 From 56f7578fbb182f21eaa2ea3742fefdec1f878c71 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 7 Jun 2013 13:46:39 +0100 Subject: Remove redundant workaround. --- src/vm_memory_monitor.erl | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index f70156b6..f281c16c 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -263,29 +263,11 @@ get_total_memory({unix,openbsd}) -> sysctl("hw.usermem"); get_total_memory({win32,_OSname}) -> - %% Due to the Erlang print format bug, on Windows boxes the memory - %% size is broken. For example Windows 7 64 bit with 4Gigs of RAM - %% we get negative memory size: - %% > os_mon_sysinfo:get_mem_info(). - %% ["76 -1658880 1016913920 -1 -1021628416 2147352576 2134794240\n"] - %% Due to this bug, we don't actually know anything. Even if the - %% number is postive we can't be sure if it's correct. This only - %% affects us on os_mon versions prior to 2.2.1. - case application:get_key(os_mon, vsn) of - undefined -> - unknown; - {ok, Version} -> - case rabbit_misc:version_compare(Version, "2.2.1", lt) of - true -> %% os_mon is < 2.2.1, so we know nothing - unknown; - false -> - [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 - end - end; + [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; get_total_memory({unix, linux}) -> File = read_proc_file("/proc/meminfo"), -- cgit v1.2.1 From d53d4da3b4b26cd46b1443eb7c854687285efab4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 11 Jun 2013 12:38:03 +0100 Subject: Remove some vestigial events. --- src/rabbit_mirror_queue_misc.erl | 2 -- src/rabbit_mirror_queue_slave.erl | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'src') diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 5607bfa9..529351a2 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -204,8 +204,6 @@ start_child(Name, MirrorNode, Q) -> report_deaths(_MirrorPid, _IsMaster, _QueueName, []) -> ok; report_deaths(MirrorPid, IsMaster, QueueName, DeadPids) -> - rabbit_event:notify(queue_mirror_deaths, [{name, QueueName}, - {pids, DeadPids}]), rabbit_log:info("Mirrored-queue (~s): ~s ~s saw deaths of mirrors ~s~n", [rabbit_misc:rs(QueueName), case IsMaster of diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 964b0eb4..49ba94ee 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -41,15 +41,13 @@ %%---------------------------------------------------------------------------- --define(CREATION_EVENT_KEYS, +-define(INFO_KEYS, [pid, name, master_pid, is_synchronised ]). --define(INFO_KEYS, ?CREATION_EVENT_KEYS). - -define(SYNC_INTERVAL, 25). %% milliseconds -define(RAM_DURATION_UPDATE_INTERVAL, 5000). -define(DEATH_TIMEOUT, 20000). %% 20 seconds @@ -124,8 +122,6 @@ init(Q = #amqqueue { name = QName }) -> depth_delta = undefined }, - rabbit_event:notify(queue_slave_created, - infos(?CREATION_EVENT_KEYS, State)), ok = gm:broadcast(GM, request_depth), {ok, State, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, -- cgit v1.2.1 From 92517a8839fbe49e13e3a2de1ccb8ada181706e0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 12 Jun 2013 12:06:23 +0100 Subject: "This can be removed after 3.1.0 is released." -- well, let's do that then. --- src/rabbit_mnesia.erl | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src') diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6ea91086..6eda32a3 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -777,11 +777,6 @@ version_error(Name, This, Remote) -> check_otp_consistency(Remote) -> check_version_consistency(erlang:system_info(otp_release), Remote, "OTP"). -%% Unlike the rest of 3.0.x, 3.0.0 is not compatible. This can be -%% removed after 3.1.0 is released. -check_rabbit_consistency("3.0.0") -> - version_error("Rabbit", rabbit_misc:version(), "3.0.0"); - check_rabbit_consistency(Remote) -> check_version_consistency( rabbit_misc:version(), Remote, "Rabbit", -- cgit v1.2.1 From bc9a6352916be69edbcb31c3de2879091c066b0d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Jun 2013 12:29:30 +0100 Subject: Eliminate ?MEMORY_LIMIT_SCALING and make the ratio at which we page configurable. Note that I have changed the representation from a ratio-of-a-ratio to just a plain ratio (i.e. propertion of total memory, not proportion of the high watermark). I believe this will be easier to understand. Hence also the name vm_memory_paging_watermark, chosen by analogy with vm_memory_high_watermark. --- src/rabbit_memory_monitor.erl | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index 117ff95a..a2df255c 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -43,17 +43,6 @@ -define(DEFAULT_UPDATE_INTERVAL, 2500). -define(TABLE_NAME, ?MODULE). -%% Because we have a feedback loop here, we need to ensure that we -%% have some space for when the queues don't quite respond as fast as -%% we would like, or when there is buffering going on in other parts -%% of the system. In short, we aim to stay some distance away from -%% when the memory alarms will go off, which cause backpressure (of -%% some sort) on producers. Note that all other Thresholds are -%% relative to this scaling. --define(MEMORY_LIMIT_SCALING, 0.4). - --define(LIMIT_THRESHOLD, 0.5). %% don't limit queues when mem use is < this - %% If all queues are pushed to disk (duration 0), then the sum of %% their reported lengths will be 0. If memory then becomes available, %% unless we manually intervene, the sum will remain 0, and the queues @@ -207,15 +196,13 @@ internal_update(State = #state { queue_durations = Durations, desired_duration = DesiredDurationAvg, queue_duration_sum = Sum, queue_duration_count = Count }) -> - MemoryLimit = ?MEMORY_LIMIT_SCALING * vm_memory_monitor:get_memory_limit(), - MemoryRatio = case MemoryLimit > 0.0 of - true -> erlang:memory(total) / MemoryLimit; - false -> infinity - end, + {ok, LimitThreshold} = + application:get_env(rabbit, vm_memory_paging_watermark), + MemoryRatio = erlang:memory(total) / vm_memory_monitor:get_total_memory(), DesiredDurationAvg1 = if MemoryRatio =:= infinity -> 0.0; - MemoryRatio < ?LIMIT_THRESHOLD orelse Count == 0 -> + MemoryRatio < LimitThreshold orelse Count == 0 -> infinity; MemoryRatio < ?SUM_INC_THRESHOLD -> ((Sum + ?SUM_INC_AMOUNT) / Count) / MemoryRatio; -- cgit v1.2.1 From bfc817b58e3883f15e586d426bce33a1d209bb10 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Jun 2013 13:33:43 +0100 Subject: Matthias convinced me to go with ratio-of-a-ratio. --- src/rabbit_memory_monitor.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index a2df255c..d47b6e30 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -197,8 +197,8 @@ internal_update(State = #state { queue_durations = Durations, queue_duration_sum = Sum, queue_duration_count = Count }) -> {ok, LimitThreshold} = - application:get_env(rabbit, vm_memory_paging_watermark), - MemoryRatio = erlang:memory(total) / vm_memory_monitor:get_total_memory(), + application:get_env(rabbit, vm_memory_high_watermark_paging_ratio), + MemoryRatio = erlang:memory(total) / vm_memory_monitor:get_memory_limit(), DesiredDurationAvg1 = if MemoryRatio =:= infinity -> 0.0; -- cgit v1.2.1 From 9a9905e0b90124b92e0afc474b69c9a9fbfc2ea2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Jun 2013 17:45:13 +0100 Subject: Oops, I forgot that going back to vm_memory_monitor:get_memory_limit() means we can now have a limit of 0.0. --- src/rabbit_memory_monitor.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index d47b6e30..a9339074 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -198,7 +198,11 @@ internal_update(State = #state { queue_durations = Durations, queue_duration_count = Count }) -> {ok, LimitThreshold} = application:get_env(rabbit, vm_memory_high_watermark_paging_ratio), - MemoryRatio = erlang:memory(total) / vm_memory_monitor:get_memory_limit(), + MemoryLimit = vm_memory_monitor:get_memory_limit(), + MemoryRatio = case MemoryLimit > 0.0 of + true -> erlang:memory(total) / MemoryLimit; + false -> infinity + end, DesiredDurationAvg1 = if MemoryRatio =:= infinity -> 0.0; -- cgit v1.2.1 From 6ae3c5917807effb509ddaf72abf201a16f27b67 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 19 Jun 2013 15:57:00 +0400 Subject: Don't crash when an alarm is cleared when there's nothing to clear --- src/rabbit_alarm.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 17f1edcf..07247573 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -145,7 +145,12 @@ dict_unappend_all(Key, _Val, Dict) -> dict:erase(Key, Dict). dict_unappend(Key, Val, Dict) -> - case lists:delete(Val, dict:fetch(Key, Dict)) of + L = case dict:find(Key, Dict) of + {ok, V} -> V; + error -> [] + end, + + case lists:delete(Val, L) of [] -> dict:erase(Key, Dict); X -> dict:store(Key, X, Dict) end. -- cgit v1.2.1 From c51b1af576e759b11cf6a3f527397e8434f65aab Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 19 Jun 2013 16:47:33 +0400 Subject: Make sure set_alarm is idempotent By making sure alarms list is unique. --- src/rabbit_alarm.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 07247573..93997a37 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -104,7 +104,8 @@ handle_call(_Request, State) -> {ok, not_understood, State}. handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> - handle_set_alarm(Alarm, State#alarms{alarms = [Alarm|Alarms]}); + UpdatedAlarms = lists:usort([Alarm|Alarms]), + handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}); handle_event({clear_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> handle_clear_alarm(Alarm, State#alarms{alarms = lists:keydelete(Alarm, 1, -- cgit v1.2.1 From 18ec96b321e4bb9db0ec2535bd9607641d217f83 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 20 Jun 2013 17:36:10 +0400 Subject: Make sure the list of alarm resources is unique --- src/rabbit_alarm.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 93997a37..000c1e2a 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -142,6 +142,13 @@ code_change(_OldVsn, State, _Extra) -> %%---------------------------------------------------------------------------- +dict_append(Key, Val, Dict) -> + L = case dict:find(Key, Dict) of + {ok, V} -> V; + error -> [] + end, + dict:store(Key, lists:usort([Val|L]), Dict). + dict_unappend_all(Key, _Val, Dict) -> dict:erase(Key, Dict). @@ -220,7 +227,7 @@ handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> "*** Publishers will be blocked until this alarm clears ***~n" "**********************************************************~n", [Source, Node]), - {ok, maybe_alert(fun dict:append/3, Node, Source, State)}; + {ok, maybe_alert(fun dict_append/3, Node, Source, State)}; handle_set_alarm({file_descriptor_limit, []}, State) -> rabbit_log:warning( "file descriptor limit alarm set.~n~n" -- cgit v1.2.1 From d984ee3a6d2f3480744c4b013e049e138267d670 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 20 Jun 2013 18:46:11 +0400 Subject: Don't log duplicate alarm warnings --- src/rabbit_alarm.erl | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 000c1e2a..78098443 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -104,12 +104,15 @@ handle_call(_Request, State) -> {ok, not_understood, State}. handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> + IsDuplicate = lists:member(Alarm, Alarms), UpdatedAlarms = lists:usort([Alarm|Alarms]), - handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}); + handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}, IsDuplicate); handle_event({clear_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> + IsDuplicate = not lists:member(Alarm, Alarms), handle_clear_alarm(Alarm, State#alarms{alarms = lists:keydelete(Alarm, 1, - Alarms)}); + Alarms)}, + IsDuplicate); handle_event({node_up, Node}, State) -> %% Must do this via notify and not call to avoid possible deadlock. @@ -220,7 +223,7 @@ internal_register(Pid, {M, F, A} = AlertMFA, NewAlertees = dict:store(Pid, AlertMFA, Alertees), State#alarms{alertees = NewAlertees}. -handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> +handle_set_alarm({{resource_limit, Source, Node}, []}, State, false) -> rabbit_log:warning( "~s resource limit alarm set on node ~p.~n~n" "**********************************************************~n" @@ -228,24 +231,32 @@ handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> "**********************************************************~n", [Source, Node]), {ok, maybe_alert(fun dict_append/3, Node, Source, State)}; -handle_set_alarm({file_descriptor_limit, []}, State) -> +handle_set_alarm({{resource_limit, Source, Node}, []}, State, true) -> + {ok, maybe_alert(fun dict_append/3, Node, Source, State)}; +handle_set_alarm({file_descriptor_limit, []}, State, false) -> rabbit_log:warning( "file descriptor limit alarm set.~n~n" "********************************************************************~n" "*** New connections will not be accepted until this alarm clears ***~n" "********************************************************************~n"), {ok, State}; -handle_set_alarm(Alarm, State) -> +handle_set_alarm({file_descriptor_limit, []}, State, true) -> + {ok, State}; +handle_set_alarm(Alarm, State, _IsDuplicate) -> rabbit_log:warning("alarm '~p' set~n", [Alarm]), {ok, State}. -handle_clear_alarm({resource_limit, Source, Node}, State) -> +handle_clear_alarm({resource_limit, Source, Node}, State, false) -> rabbit_log:warning("~s resource limit alarm cleared on node ~p~n", [Source, Node]), {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; -handle_clear_alarm(file_descriptor_limit, State) -> +handle_clear_alarm({resource_limit, Source, Node}, State, true) -> + {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; +handle_clear_alarm(file_descriptor_limit, State, false) -> rabbit_log:warning("file descriptor limit alarm cleared~n"), {ok, State}; -handle_clear_alarm(Alarm, State) -> +handle_clear_alarm(file_descriptor_limit, State, true) -> + {ok, State}; +handle_clear_alarm(Alarm, State, _IsDuplicate) -> rabbit_log:warning("alarm '~p' cleared~n", [Alarm]), {ok, State}. -- cgit v1.2.1 From 195d184f7d0bc1bca07c5daa4f43b3d21603aa6e Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 21 Jun 2013 00:02:48 +0400 Subject: Correctly determine duplicate alarms when alarm is cleared Also, don't bother doing anything on duplicate alarms, per review feedback. --- src/rabbit_alarm.erl | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 78098443..f3fa1962 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -105,14 +105,25 @@ handle_call(_Request, State) -> handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> IsDuplicate = lists:member(Alarm, Alarms), - UpdatedAlarms = lists:usort([Alarm|Alarms]), - handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}, IsDuplicate); + case IsDuplicate of + true -> + {ok, State}; + false -> + UpdatedAlarms = lists:usort([Alarm|Alarms]), + handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}) + end; handle_event({clear_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> - IsDuplicate = not lists:member(Alarm, Alarms), - handle_clear_alarm(Alarm, State#alarms{alarms = lists:keydelete(Alarm, 1, - Alarms)}, - IsDuplicate); + ExistingAlarm = lists:member(Alarm, [Res || {Res, _} <- Alarms]), + case ExistingAlarm of + true -> + handle_clear_alarm(Alarm, + State#alarms{alarms = lists:keydelete(Alarm, 1, + Alarms)}); + false -> + {ok, State} + + end; handle_event({node_up, Node}, State) -> %% Must do this via notify and not call to avoid possible deadlock. @@ -223,7 +234,7 @@ internal_register(Pid, {M, F, A} = AlertMFA, NewAlertees = dict:store(Pid, AlertMFA, Alertees), State#alarms{alertees = NewAlertees}. -handle_set_alarm({{resource_limit, Source, Node}, []}, State, false) -> +handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> rabbit_log:warning( "~s resource limit alarm set on node ~p.~n~n" "**********************************************************~n" @@ -231,32 +242,24 @@ handle_set_alarm({{resource_limit, Source, Node}, []}, State, false) -> "**********************************************************~n", [Source, Node]), {ok, maybe_alert(fun dict_append/3, Node, Source, State)}; -handle_set_alarm({{resource_limit, Source, Node}, []}, State, true) -> - {ok, maybe_alert(fun dict_append/3, Node, Source, State)}; -handle_set_alarm({file_descriptor_limit, []}, State, false) -> +handle_set_alarm({file_descriptor_limit, []}, State) -> rabbit_log:warning( "file descriptor limit alarm set.~n~n" "********************************************************************~n" "*** New connections will not be accepted until this alarm clears ***~n" "********************************************************************~n"), {ok, State}; -handle_set_alarm({file_descriptor_limit, []}, State, true) -> - {ok, State}; -handle_set_alarm(Alarm, State, _IsDuplicate) -> +handle_set_alarm(Alarm, State) -> rabbit_log:warning("alarm '~p' set~n", [Alarm]), {ok, State}. -handle_clear_alarm({resource_limit, Source, Node}, State, false) -> +handle_clear_alarm({resource_limit, Source, Node}, State) -> rabbit_log:warning("~s resource limit alarm cleared on node ~p~n", [Source, Node]), {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; -handle_clear_alarm({resource_limit, Source, Node}, State, true) -> - {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; -handle_clear_alarm(file_descriptor_limit, State, false) -> +handle_clear_alarm(file_descriptor_limit, State) -> rabbit_log:warning("file descriptor limit alarm cleared~n"), {ok, State}; -handle_clear_alarm(file_descriptor_limit, State, true) -> - {ok, State}; -handle_clear_alarm(Alarm, State, _IsDuplicate) -> +handle_clear_alarm(Alarm, State) -> rabbit_log:warning("alarm '~p' cleared~n", [Alarm]), {ok, State}. -- cgit v1.2.1 From 5700ae9eff64fab4b671f1bc443e8283d84a94c6 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 21 Jun 2013 00:13:08 +0400 Subject: Use lists:keymember here --- src/rabbit_alarm.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index f3fa1962..16b69af9 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -114,7 +114,7 @@ handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> end; handle_event({clear_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> - ExistingAlarm = lists:member(Alarm, [Res || {Res, _} <- Alarms]), + ExistingAlarm = lists:keymember(Alarm, 1, Alarms), case ExistingAlarm of true -> handle_clear_alarm(Alarm, -- cgit v1.2.1 From 7b88b47870f487bef9ccdab25883582943613c10 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 24 Jun 2013 17:43:46 +0400 Subject: Deliver all alart notification to handle overlapping alarms connection.blocked requires us to track resources we are conserving. This means the old logic of determining edge state transitions for alarms does not work any more. Instead of using the old strategy of comparing alarmed node collection sizes, instead of pass around what event the notification is for and simply deliver it to the relevant nodes. This requires that rabbit_alarm event consumers handle duplicate notifications. They already do so after earlier changes on branch bug25191. This makes connection unblocking work correctly in the following sequence of events: * memory alarm set for node A * disk alarm set for node A * memory alarm cleared for node A * disk alarm cleared for node A as well as other similar scenarios with overlapping alarms. This slighly increases internode and intranode message traffic of alarm notifications. Since alarms occur rarely in well-monitored systems, this is a reasonable trade-off. --- src/rabbit_alarm.erl | 34 ++++++++++++---------------------- src/rabbit_reader.erl | 5 +++-- 2 files changed, 15 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 16b69af9..40be1951 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -133,7 +133,7 @@ handle_event({node_up, Node}, State) -> {ok, State}; handle_event({node_down, Node}, State) -> - {ok, maybe_alert(fun dict_unappend_all/3, Node, [], State)}; + {ok, maybe_alert(fun dict_unappend_all/3, Node, [], clear, State)}; handle_event({register, Pid, AlertMFA}, State) -> {ok, internal_register(Pid, AlertMFA, State)}; @@ -177,35 +177,25 @@ dict_unappend(Key, Val, Dict) -> X -> dict:store(Key, X, Dict) end. -count_dict_values(Val, Dict) -> - dict:fold(fun (_Node, List, Count) -> - Count + case lists:member(Val, List) of - true -> 1; - false -> 0 - end - end, 0, Dict). - -maybe_alert(UpdateFun, Node, Source, +maybe_alert(UpdateFun, Node, Source, Event, State = #alarms{alarmed_nodes = AN, alertees = Alertees}) -> AN1 = UpdateFun(Node, Source, AN), - BeforeSz = count_dict_values(Source, AN), - AfterSz = count_dict_values(Source, AN1), %% If we have changed our alarm state, inform the remotes. IsLocal = Node =:= node(), - if IsLocal andalso BeforeSz < AfterSz -> + if IsLocal andalso Event =:= set -> ok = alert_remote(true, Alertees, Source); - IsLocal andalso BeforeSz > AfterSz -> + IsLocal andalso Event =:= clear -> ok = alert_remote(false, Alertees, Source); - true -> + true -> ok end, - %% If the overall alarm state has changed, inform the locals. - case {dict:size(AN), dict:size(AN1)} of - {0, 1} -> ok = alert_local(true, Alertees, Source); - {1, 0} -> ok = alert_local(false, Alertees, Source); - {_, _} -> ok + case Event of + clear -> + ok = alert_local(false, Alertees, Source); + set -> + ok = alert_local(true, Alertees, Source) end, State#alarms{alarmed_nodes = AN1}. @@ -241,7 +231,7 @@ handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> "*** Publishers will be blocked until this alarm clears ***~n" "**********************************************************~n", [Source, Node]), - {ok, maybe_alert(fun dict_append/3, Node, Source, State)}; + {ok, maybe_alert(fun dict_append/3, Node, Source, set, State)}; handle_set_alarm({file_descriptor_limit, []}, State) -> rabbit_log:warning( "file descriptor limit alarm set.~n~n" @@ -256,7 +246,7 @@ handle_set_alarm(Alarm, State) -> handle_clear_alarm({resource_limit, Source, Node}, State) -> rabbit_log:warning("~s resource limit alarm cleared on node ~p~n", [Source, Node]), - {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; + {ok, maybe_alert(fun dict_unappend/3, Node, Source, clear, State)}; handle_clear_alarm(file_descriptor_limit, State) -> rabbit_log:warning("file descriptor limit alarm cleared~n"), {ok, State}; diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index a2727067..31403ab8 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -416,8 +416,9 @@ terminate(_Explanation, State) -> {force, State}. control_throttle(State = #v1{connection_state = CS, throttle = Throttle}) -> - case {CS, ((Throttle#throttle.conserve_resources =/= []) orelse - credit_flow:blocked())} of + IsThrottled = ((Throttle#throttle.conserve_resources =/= []) orelse + credit_flow:blocked()), + case {CS, IsThrottled} of {running, true} -> State#v1{connection_state = blocking}; {blocking, false} -> State#v1{connection_state = running}; {blocked, false} -> ok = rabbit_heartbeat:resume_monitor( -- cgit v1.2.1 From e127bf47ca80bfbc1b5f098920bf7c7deb66f48d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 24 Jun 2013 16:58:07 +0100 Subject: Most of policy apply-to. --- src/rabbit_policy.erl | 34 +++++++++++++++++++++++++++++----- src/rabbit_upgrade_functions.erl | 11 +++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 0990c662..4c5323f9 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -27,7 +27,7 @@ -export([register/0]). -export([name/1, get/2, set/1]). -export([validate/4, notify/4, notify_clear/3]). --export([parse_set/5, set/5, delete/2, lookup/2, list/0, list/1, +-export([parse_set/5, set/6, delete/2, lookup/2, list/0, list/1, list_formatted/1, info_keys/0]). -rabbit_boot_step({?MODULE, @@ -88,16 +88,24 @@ parse_set0(VHost, Name, Pattern, Defn, Priority) -> {error_string, "JSON decoding error"} end. -set(VHost, Name, Pattern, Definition, Priority) -> +set(VHost, Name, Pattern, Definition, Priority, ApplyTo) -> PolicyProps = [{<<"pattern">>, Pattern}, {<<"definition">>, Definition}, {<<"priority">>, case Priority of undefined -> 0; _ -> Priority + end}, + {<<"apply-to">>, case ApplyTo of + undefined -> 0; + _ -> ApplyTo end}], set0(VHost, Name, PolicyProps). -set0(VHost, Name, Term) -> +set0(VHost, Name, Term0) -> + Term = case pget(<<"apply-to">>, Term0) of + undefined -> [{<<"apply-to">>, <<"both">>} | Term0]; + _ -> Term0 + end, rabbit_runtime_parameters:set_any(VHost, <<"policy">>, Name, Term). delete(VHost, Name) -> @@ -130,6 +138,7 @@ p(Parameter, DefnFun) -> [{vhost, pget(vhost, Parameter)}, {name, pget(name, Parameter)}, {pattern, pget(<<"pattern">>, Value)}, + {'apply-to', pget(<<"apply-to">>, Value)}, {definition, DefnFun(pget(<<"definition">>, Value))}, {priority, pget(<<"priority">>, Value)}]. @@ -202,8 +211,15 @@ match(Name, Policies) -> [Policy | _Rest] -> Policy end. -matches(#resource{name = Name}, Policy) -> - match =:= re:run(Name, pget(pattern, Policy), [{capture, none}]). +matches(#resource{name = Name, kind = Kind}, Policy) -> + matches_type(Kind, pget('apply-to', Policy)) andalso + match =:= re:run(Name, pget(pattern, Policy), [{capture, none}]). + +matches_type(exchange, <<"exchanges">>) -> true; +matches_type(queue, <<"queues">>) -> true; +matches_type(exchange, <<"both">>) -> true; +matches_type(queue, <<"both">>) -> true; +matches_type(_, _) -> false. sort_pred(A, B) -> pget(priority, A) >= pget(priority, B). @@ -212,6 +228,7 @@ sort_pred(A, B) -> pget(priority, A) >= pget(priority, B). policy_validation() -> [{<<"priority">>, fun rabbit_parameter_validation:number/2, mandatory}, {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, + {<<"apply-to">>, fun apply_to_validation/2, optional}, {<<"definition">>, fun validation/2, mandatory}]. validation(_Name, []) -> @@ -257,3 +274,10 @@ a2b(A) -> list_to_binary(atom_to_list(A)). dups(L) -> L -- lists:usort(L). is_proplist(L) -> length(L) =:= length([I || I = {_, _} <- L]). + +apply_to_validation(_Name, <<"both">>) -> ok; +apply_to_validation(_Name, <<"exchanges">>) -> ok; +apply_to_validation(_Name, <<"queues">>) -> ok; +apply_to_validation(_Name, Term) -> + {error, "apply-to '~s' unrecognised; should be 'queues', 'exchanges' " + "or 'both'", [Term]}. diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index b7b1635b..d76002dd 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -44,6 +44,7 @@ -rabbit_upgrade({no_mirror_nodes, mnesia, [sync_slave_pids]}). -rabbit_upgrade({gm_pids, mnesia, [no_mirror_nodes]}). -rabbit_upgrade({exchange_decorators, mnesia, [policy]}). +-rabbit_upgrade({policy_apply_to, mnesia, [runtime_parameters]}). %% ------------------------------------------------------------------- @@ -70,6 +71,7 @@ -spec(no_mirror_nodes/0 :: () -> 'ok'). -spec(gm_pids/0 :: () -> 'ok'). -spec(exchange_decorators/0 :: () -> 'ok'). +-spec(policy_apply_to/0 :: () -> 'ok'). -endif. @@ -299,6 +301,15 @@ exchange_decorators(Table) -> [name, type, durable, auto_delete, internal, arguments, scratches, policy, decorators]). +policy_apply_to() -> + transform( + rabbit_runtime_parameters, + fun ({runtime_parameters, Key = {_VHost, <<"policy">>, _Name}, Value}) -> + {runtime_parameters, Key, [{<<"apply-to">>, <<"both">>} | Value]}; + ({runtime_parameters, Key, Value}) -> + {runtime_parameters, Key, Value} + end, + [key, value]). %%-------------------------------------------------------------------- -- cgit v1.2.1 From 7745cbd876ae61344a3a8485c9456b39a10605cc Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Jun 2013 16:31:39 +0100 Subject: Convert the x-purpose hack into a bit less of a hack: consumer priorities. --- src/priority_queue.erl | 30 ++++++++++++++-- src/rabbit_amqqueue_process.erl | 80 +++++++++++++++++++---------------------- 2 files changed, 64 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 18e1e8d9..0ffd208a 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -40,8 +40,8 @@ -module(priority_queue). --export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, in/2, in/3, - out/1, join/2]). +-export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, from_list/1, + in/2, in/3, out/1, join/2, filter/2, fold/3]). %%---------------------------------------------------------------------------- @@ -59,10 +59,13 @@ -spec(is_empty/1 :: (pqueue()) -> boolean()). -spec(len/1 :: (pqueue()) -> non_neg_integer()). -spec(to_list/1 :: (pqueue()) -> [{priority(), any()}]). +-spec(from_list/1 :: ([{priority(), any()}]) -> pqueue()). -spec(in/2 :: (any(), pqueue()) -> pqueue()). -spec(in/3 :: (any(), priority(), pqueue()) -> pqueue()). -spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}). -spec(join/2 :: (pqueue(), pqueue()) -> pqueue()). +-spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()). +-spec(fold/3 :: (fun ((any(), any()) -> any()), any(), pqueue()) -> any()). -endif. @@ -96,6 +99,9 @@ to_list({pqueue, Queues}) -> [{maybe_negate_priority(P), V} || {P, Q} <- Queues, {0, V} <- to_list(Q)]. +from_list(L) -> + lists:foldl(fun ({P, E}, Q) -> in(E, P, Q) end, new(), L). + in(Item, Q) -> in(Item, 0, Q). @@ -147,6 +153,14 @@ out({pqueue, [{P, Q} | Queues]}) -> end, {R, NewQ}. +out_p({queue, _, _, _} = Q) -> add_p(out(Q), 0); +out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), P). + +add_p(R, P) -> case R of + {empty, Q} -> {empty, Q}; + {{value, V}, Q} -> {{value, V, P}, Q} + end. + join(A, {queue, [], [], 0}) -> A; join({queue, [], [], 0}, B) -> @@ -185,6 +199,18 @@ merge([{PA, A}|As], Bs = [{PB, _}|_], Acc) when PA < PB orelse PA == infinity -> merge(As = [{_, _}|_], [{PB, B}|Bs], Acc) -> merge(As, Bs, [ {PB, B} | Acc ]). +filter(Pred, Q) -> fold(fun(V, P, Acc) -> + case Pred(V) of + true -> in(V, P, Acc); + false -> Acc + end + end, new(), Q). + +fold(Fun, Init, Q) -> case out_p(Q) of + {empty, _Q} -> Init; + {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1) + end. + r2f([], 0) -> {queue, [], [], 0}; r2f([_] = R, 1) -> {queue, [], R, 1}; r2f([X,Y], 2) -> {queue, [X], [Y], 2}; diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index bafc12dd..e3eeded9 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -145,7 +145,7 @@ init_state(Q) -> State = #q{q = Q, exclusive_consumer = none, has_had_consumers = false, - active_consumers = queue:new(), + active_consumers = priority_queue:new(), senders = pmon:new(), msg_id_to_channel = gb_trees:empty(), status = running}, @@ -360,7 +360,7 @@ ensure_stats_timer(State) -> rabbit_event:ensure_stats_timer(State, #q.stats_timer, emit_stats). assert_invariant(State = #q{active_consumers = AC}) -> - true = (queue:is_empty(AC) orelse is_empty(State)). + true = (priority_queue:is_empty(AC) orelse is_empty(State)). is_empty(#q{backing_queue = BQ, backing_queue_state = BQS}) -> BQ:is_empty(BQS). @@ -379,7 +379,7 @@ ch_record(ChPid, LimiterPid) -> monitor_ref = MonitorRef, acktags = queue:new(), consumer_count = 0, - blocked_consumers = queue:new(), + blocked_consumers = priority_queue:new(), limiter = Limiter, unsent_message_count = 0}, put(Key, C), @@ -408,7 +408,8 @@ erase_ch_record(#cr{ch_pid = ChPid, monitor_ref = MonitorRef}) -> all_ch_record() -> [C || {{ch, _}, C} <- get()]. block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry) -> - update_ch_record(C#cr{blocked_consumers = queue:in(QEntry, Blocked)}). + Blocked1 = priority_queue:in(QEntry, consumer_priority(QEntry), Blocked), + update_ch_record(C#cr{blocked_consumers = Blocked1}). is_ch_blocked(#cr{unsent_message_count = Count, limiter = Limiter}) -> Count >= ?UNSENT_MESSAGE_LIMIT orelse rabbit_limiter:is_suspended(Limiter). @@ -432,7 +433,7 @@ deliver_msgs_to_consumers(_DeliverFun, true, State) -> {true, State}; deliver_msgs_to_consumers(DeliverFun, false, State = #q{active_consumers = ActiveConsumers}) -> - case pick_consumer(ActiveConsumers) of + case priority_queue:out(ActiveConsumers) of {empty, _} -> {false, State}; {{value, QEntry}, Tail} -> @@ -456,7 +457,8 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> notify_federation(State), {false, State}; {continue, Limiter} -> - AC1 = queue:in(E, State#q.active_consumers), + AC1 = priority_queue:in(E, consumer_priority(E), + State#q.active_consumers), deliver_msg_to_consumer( DeliverFun, Consumer, C#cr{limiter = Limiter}, State#q{active_consumers = AC1}) @@ -549,26 +551,14 @@ notify_federation(#q{q = Q, end. active_unfederated(Cs) -> - case queue:out(Cs) of - {empty, _} -> false; - {{value, C}, Cs1} -> case federated_consumer(C) of - true -> active_unfederated(Cs1); - false -> true - end - end. - -%% TODO this could be more efficient. But we'd need another representation, -%% and thus to abstract the representation of active_consumers. -pick_consumer(Cs) -> - case lists:splitwith(fun federated_consumer/1, queue:to_list(Cs)) of - {_, []} -> queue:out(Cs); - {Feds, [UnFed|Tl]} -> queue:out(queue:from_list([UnFed | Feds ++ Tl])) - end. - -federated_consumer({_Pid, #consumer{args = Args}}) -> - case rabbit_misc:table_lookup(Args, <<"x-purpose">>) of - {longstr, <<"federation">>} -> true; - _ -> false + %% TODO could this be faster? + lists:any(fun ({Priority, _Consumer}) -> Priority < 0 end, + priority_queue:to_list(Cs)). + +consumer_priority({_ChPid, #consumer{args = Args}}) -> + case rabbit_misc:table_lookup(Args, <<"x-priority">>) of + {_, Priority} -> Priority; + _ -> 0 end. attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, @@ -668,17 +658,17 @@ requeue(AckTags, ChPid, State) -> fun (State1) -> requeue_and_run(AckTags, State1) end). remove_consumer(ChPid, ConsumerTag, Queue) -> - queue:filter(fun ({CP, #consumer{tag = CTag}}) -> - (CP /= ChPid) or (CTag /= ConsumerTag) - end, Queue). + priority_queue:filter(fun ({CP, #consumer{tag = CTag}}) -> + (CP /= ChPid) or (CTag /= ConsumerTag) + end, Queue). remove_consumers(ChPid, Queue, QName) -> - queue:filter(fun ({CP, #consumer{tag = CTag}}) when CP =:= ChPid -> - emit_consumer_deleted(ChPid, CTag, QName), - false; - (_) -> - true - end, Queue). + priority_queue:filter(fun ({CP, #consumer{tag = CTag}}) when CP =:= ChPid -> + emit_consumer_deleted(ChPid, CTag, QName), + false; + (_) -> + true + end, Queue). possibly_unblock(State, ChPid, Update) -> case lookup_ch(ChPid) of @@ -693,17 +683,17 @@ possibly_unblock(State, ChPid, Update) -> unblock(State, C = #cr{limiter = Limiter}) -> case lists:partition( - fun({_ChPid, #consumer{tag = CTag}}) -> + fun({_P, {_ChPid, #consumer{tag = CTag}}}) -> rabbit_limiter:is_consumer_blocked(Limiter, CTag) - end, queue:to_list(C#cr.blocked_consumers)) of + end, priority_queue:to_list(C#cr.blocked_consumers)) of {_, []} -> update_ch_record(C), State; {Blocked, Unblocked} -> - BlockedQ = queue:from_list(Blocked), - UnblockedQ = queue:from_list(Unblocked), + BlockedQ = priority_queue:from_list(Blocked), + UnblockedQ = priority_queue:from_list(Unblocked), update_ch_record(C#cr{blocked_consumers = BlockedQ}), - AC1 = queue:join(State#q.active_consumers, UnblockedQ), + AC1 = priority_queue:join(State#q.active_consumers, UnblockedQ), run_message_queue(State#q{active_consumers = AC1}) end. @@ -1041,9 +1031,9 @@ consumers(#q{active_consumers = ActiveConsumers}) -> consumers(ActiveConsumers, []), all_ch_record()). consumers(Consumers, Acc) -> - rabbit_misc:queue_fold( - fun ({ChPid, #consumer{tag = CTag, ack_required = AckRequired}}, Acc1) -> - [{ChPid, CTag, AckRequired} | Acc1] + priority_queue:fold( + fun ({ChPid, #consumer{tag = CTag, ack_required = AckReq}}, _P, Acc1) -> + [{ChPid, CTag, AckReq} | Acc1] end, Acc, Consumers). emit_stats(State) -> @@ -1208,7 +1198,9 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, ok = maybe_send_reply(ChPid, OkMsg), emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, not NoAck, qname(State1)), - AC1 = queue:in({ChPid, Consumer}, State1#q.active_consumers), + AC1 = priority_queue:in({ChPid, Consumer}, + consumer_priority({ChPid, Consumer}), + State1#q.active_consumers), reply(ok, run_message_queue(State1#q{active_consumers = AC1})) end; -- cgit v1.2.1 From d8b3b025dee868c68a77774916d8549f3838a32d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Jun 2013 16:39:09 +0100 Subject: Oops --- src/priority_queue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 0ffd208a..3c46c4ce 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -154,7 +154,7 @@ out({pqueue, [{P, Q} | Queues]}) -> {R, NewQ}. out_p({queue, _, _, _} = Q) -> add_p(out(Q), 0); -out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), P). +out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), maybe_negate_priority(P)). add_p(R, P) -> case R of {empty, Q} -> {empty, Q}; -- cgit v1.2.1 From a9b9dc61e5aa4f0cbd04eb1de69d2e4540968a10 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Jun 2013 16:43:14 +0100 Subject: TODO-- yes it could be faster. --- src/priority_queue.erl | 6 +++++- src/rabbit_amqqueue_process.erl | 5 +---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 3c46c4ce..572bca95 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -41,7 +41,7 @@ -module(priority_queue). -export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, from_list/1, - in/2, in/3, out/1, join/2, filter/2, fold/3]). + in/2, in/3, out/1, join/2, filter/2, fold/3, highest/1]). %%---------------------------------------------------------------------------- @@ -66,6 +66,7 @@ -spec(join/2 :: (pqueue(), pqueue()) -> pqueue()). -spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()). -spec(fold/3 :: (fun ((any(), any()) -> any()), any(), pqueue()) -> any()). +-spec(highest/1 :: (pqueue()) -> priority()). -endif. @@ -211,6 +212,9 @@ fold(Fun, Init, Q) -> case out_p(Q) of {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1) end. +highest({queue, _, _, _}) -> 0; +highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P). + r2f([], 0) -> {queue, [], [], 0}; r2f([_] = R, 1) -> {queue, [], R, 1}; r2f([X,Y], 2) -> {queue, [X], [Y], 2}; diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e3eeded9..60b56aeb 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -550,10 +550,7 @@ notify_federation(#q{q = Q, false -> rabbit_federation_queue:stop(Q) end. -active_unfederated(Cs) -> - %% TODO could this be faster? - lists:any(fun ({Priority, _Consumer}) -> Priority < 0 end, - priority_queue:to_list(Cs)). +active_unfederated(Cs) -> priority_queue:highest(Cs) >= 0. consumer_priority({_ChPid, #consumer{args = Args}}) -> case rabbit_misc:table_lookup(Args, <<"x-priority">>) of -- cgit v1.2.1 From 9473ebe9d4e94c91ad61a35485aad6a6555be57c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 28 Jun 2013 16:37:47 +0100 Subject: Oops --- src/priority_queue.erl | 1 + src/rabbit_amqqueue_process.erl | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 572bca95..8ded389b 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -212,6 +212,7 @@ fold(Fun, Init, Q) -> case out_p(Q) of {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1) end. +highest({queue, [], [], 0}) -> exit(highest_priority_of_empty_queue); highest({queue, _, _, _}) -> 0; highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 60b56aeb..dc2a7a3f 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -550,7 +550,8 @@ notify_federation(#q{q = Q, false -> rabbit_federation_queue:stop(Q) end. -active_unfederated(Cs) -> priority_queue:highest(Cs) >= 0. +active_unfederated(Cs) -> + not priority_queue:is_empty(Cs) andalso priority_queue:highest(Cs) >= 0. consumer_priority({_ChPid, #consumer{args = Args}}) -> case rabbit_misc:table_lookup(Args, <<"x-priority">>) of -- cgit v1.2.1 From 9da1f8a0965a560b3f1adabf1048ba2c2a4d4a7e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 1 Jul 2013 13:58:27 +0100 Subject: More consistent and correct nomenclature. --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index dc2a7a3f..a0c06598 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -276,7 +276,7 @@ terminate_shutdown(Fun, State = #q{q = Q}) -> undefined -> State1; _ -> ok = rabbit_memory_monitor:deregister(self()), QName = qname(State), - rabbit_federation_queue:terminate(Q), + rabbit_federation_queue:maybe_stop(Q), [emit_consumer_deleted(Ch, CTag, QName) || {Ch, CTag, _} <- consumers(State1)], State1#q{backing_queue_state = Fun(BQS)} @@ -547,7 +547,7 @@ notify_federation(#q{q = Q, IsEmpty = BQ:is_empty(BQS), case IsEmpty andalso active_unfederated(ActiveConsumers) of true -> rabbit_federation_queue:run(Q); - false -> rabbit_federation_queue:stop(Q) + false -> rabbit_federation_queue:pause(Q) end. active_unfederated(Cs) -> -- cgit v1.2.1 From b428435664d171a998e8b51059a3739ec70e83ee Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 1 Jul 2013 14:13:27 +0100 Subject: Ensure that if we fail over / reconfigure federation while running, we ask the qproc to remoind us what our running state is. --- src/rabbit_amqqueue.erl | 6 +++++- src/rabbit_amqqueue_process.erl | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 61a1cdd5..7004a353 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -26,7 +26,7 @@ -export([list/0, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). -export([force_event_refresh/0, wake_up/1]). -export([consumers/1, consumers_all/1, consumer_info_keys/0]). --export([basic_get/4, basic_consume/10, basic_cancel/4]). +-export([basic_get/4, basic_consume/10, basic_cancel/4, notify_federation/1]). -export([notify_sent/2, notify_sent_queue_down/1, resume/2, flush_all/2]). -export([notify_down_all/2, activate_limit_all/2, credit/5]). -export([on_node_down/1]). @@ -155,6 +155,7 @@ -> rabbit_types:ok_or_error('exclusive_consume_unavailable')). -spec(basic_cancel/4 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok'). +-spec(notify_federation/1 :: (rabbit_types:amqqueue()) -> 'ok'). -spec(notify_sent/2 :: (pid(), pid()) -> 'ok'). -spec(notify_sent_queue_down/1 :: (pid()) -> 'ok'). -spec(resume/2 :: (pid(), pid()) -> 'ok'). @@ -563,6 +564,9 @@ basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, LimiterPid, LimiterActive, basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) -> delegate:call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). +notify_federation(#amqqueue{pid = QPid}) -> + delegate:cast(QPid, notify_federation). + notify_sent(QPid, ChPid) -> Key = {consumer_credit_to, QPid}, put(Key, case get(Key) of diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a0c06598..edef9af0 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1406,6 +1406,10 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, end end); +handle_cast(notify_federation, State) -> + notify_federation(State), + noreply(State); + handle_cast(wake_up, State) -> noreply(State). -- cgit v1.2.1 From 863982ef29a588777c88359fa7ab1a76bc324c4a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 1 Jul 2013 17:20:29 +0100 Subject: API change. --- src/rabbit_amqqueue_process.erl | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index edef9af0..82287619 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1145,7 +1145,6 @@ handle_call({basic_get, ChPid, NoAck, LimiterPid}, _From, State1 = ensure_expiry_timer(State), case fetch(AckRequired, State1) of {empty, State2} -> - rabbit_federation_queue:basic_get(Q), reply(empty, State2); {{Message, IsDelivered, AckTag}, State2} -> State3 = #q{backing_queue = BQ, backing_queue_state = BQS} = -- cgit v1.2.1 From 07434c2cc85974ac419ccfa848792951a807806d Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 5 Jul 2013 16:11:19 +0400 Subject: List connection.blocked in server capabilities --- src/rabbit_reader.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 31403ab8..7288d59a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -179,7 +179,8 @@ server_capabilities(rabbit_framing_amqp_0_9_1) -> [{<<"publisher_confirms">>, bool, true}, {<<"exchange_exchange_bindings">>, bool, true}, {<<"basic.nack">>, bool, true}, - {<<"consumer_cancel_notify">>, bool, true}]; + {<<"consumer_cancel_notify">>, bool, true}, + {<<"connection.blocked">>, bool, true}]; server_capabilities(_) -> []. -- cgit v1.2.1 From be90fe7b9e23e1b06b7e84301aa382fcc73c2283 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 5 Jul 2013 14:47:42 +0100 Subject: Start of queue decorators. --- src/rabbit_amqqueue.erl | 15 ++++++++++----- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_policy.erl | 14 ++++++++++---- src/rabbit_queue_decorator.erl | 34 ++++++++++++++++++++++++++++++++++ src/rabbit_registry.erl | 1 + src/rabbit_upgrade_functions.erl | 16 ++++++++++++++++ 6 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 src/rabbit_queue_decorator.erl (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 7004a353..5331a584 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -79,7 +79,8 @@ -> queue_or_absent() | rabbit_misc:thunk(queue_or_absent())). -spec(update/2 :: (name(), - fun((rabbit_types:amqqueue()) -> rabbit_types:amqqueue())) -> 'ok'). + fun((rabbit_types:amqqueue()) -> rabbit_types:amqqueue())) + -> 'not_found' | rabbit_types:amqqueue()). -spec(lookup/1 :: (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) | rabbit_types:error('not_found'); @@ -279,9 +280,10 @@ update(Name, Fun) -> case Durable of true -> ok = mnesia:write(rabbit_durable_queue, Q1, write); _ -> ok - end; + end, + Q1; [] -> - ok + not_found end. store_queue(Q = #amqqueue{durable = true}) -> @@ -295,9 +297,12 @@ store_queue(Q = #amqqueue{durable = false}) -> ok = mnesia:write(rabbit_queue, Q, write), ok. -policy_changed(Q1, Q2) -> +policy_changed(Q1 = #amqqueue{decorators = Decorators1}, + Q2 = #amqqueue{decorators = Decorators2}) -> rabbit_mirror_queue_misc:update_mirrors(Q1, Q2), - rabbit_federation_queue:policy_changed(Q1, Q2), + D1 = rabbit_queue_decorator:select(Decorators1), + D2 = rabbit_queue_decorator:select(Decorators2), + [ok = M:policy_changed(Q1, Q2) || M <- lists:usort(D1 ++ D2)], %% Make sure we emit a stats event even if nothing %% mirroring-related has changed - the policy may have changed anyway. wake_up(Q1). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c4face1f..893659d4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1139,7 +1139,7 @@ handle_call({notify_down, ChPid}, _From, State) -> end; handle_call({basic_get, ChPid, NoAck, LimiterPid}, _From, - State = #q{q = Q = #amqqueue{name = QName}}) -> + State = #q{q = #amqqueue{name = QName}}) -> AckRequired = not NoAck, State1 = ensure_expiry_timer(State), case fetch(AckRequired, State1) of diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 0990c662..28bfb9c2 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -45,7 +45,8 @@ name(#exchange{policy = Policy}) -> name0(Policy). name0(undefined) -> none; name0(Policy) -> pget(name, Policy). -set(Q = #amqqueue{name = Name}) -> Q#amqqueue{policy = set0(Name)}; +set(Q = #amqqueue{name = Name}) -> rabbit_queue_decorator:set( + Q#amqqueue{policy = set0(Name)}); set(X = #exchange{name = Name}) -> rabbit_exchange_decorator:set( X#exchange{policy = set0(Name)}). @@ -184,9 +185,14 @@ update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> update_queue(Q = #amqqueue{name = QName, policy = OldPolicy}, Policies) -> case match(QName, Policies) of OldPolicy -> no_change; - NewPolicy -> rabbit_amqqueue:update( - QName, fun(Q1) -> Q1#amqqueue{policy = NewPolicy} end), - {Q, Q#amqqueue{policy = NewPolicy}} + NewPolicy -> case rabbit_amqqueue:update( + QName, fun(Q1) -> + rabbit_queue_decorator:set( + Q1#amqqueue{policy = NewPolicy}) + end) of + #amqqueue{} = Q1 -> {Q, Q1}; + not_found -> {Q, Q } + end end. notify(no_change)-> diff --git a/src/rabbit_queue_decorator.erl b/src/rabbit_queue_decorator.erl new file mode 100644 index 00000000..9ea13beb --- /dev/null +++ b/src/rabbit_queue_decorator.erl @@ -0,0 +1,34 @@ +-module(rabbit_queue_decorator). + +-include("rabbit.hrl"). + +-export([select/1, set/1]). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-callback policy_changed(rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> + 'ok'. + +-callback active_for(rabbit_types:amqqueue()) -> boolean(). + +-else. + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [{description, 0}, {active_for, 1}, {policy_changed, 2}]; +behaviour_info(_Other) -> + undefined. + +-endif. + +%%---------------------------------------------------------------------------- + +select(Modules) -> + [M || M <- Modules, code:which(M) =/= non_existing]. + +set(Q) -> Q#amqqueue{decorators = [D || D <- list(), D:active_for(Q)]}. + +list() -> [M || {_, M} <- rabbit_registry:lookup_all(queue_decorator)]. diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 6aae8de6..5002a03d 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -130,6 +130,7 @@ class_module(exchange) -> rabbit_exchange_type; class_module(auth_mechanism) -> rabbit_auth_mechanism; class_module(runtime_parameter) -> rabbit_runtime_parameter; class_module(exchange_decorator) -> rabbit_exchange_decorator; +class_module(queue_decorator) -> rabbit_queue_decorator; class_module(policy_validator) -> rabbit_policy_validator; class_module(ha_mode) -> rabbit_mirror_queue_mode. diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index b7b1635b..9e8c1232 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -44,6 +44,7 @@ -rabbit_upgrade({no_mirror_nodes, mnesia, [sync_slave_pids]}). -rabbit_upgrade({gm_pids, mnesia, [no_mirror_nodes]}). -rabbit_upgrade({exchange_decorators, mnesia, [policy]}). +-rabbit_upgrade({queue_decorators, mnesia, [gm_pids]}). %% ------------------------------------------------------------------- @@ -70,6 +71,7 @@ -spec(no_mirror_nodes/0 :: () -> 'ok'). -spec(gm_pids/0 :: () -> 'ok'). -spec(exchange_decorators/0 :: () -> 'ok'). +-spec(queue_decorators/0 :: () -> 'ok'). -endif. @@ -299,6 +301,20 @@ exchange_decorators(Table) -> [name, type, durable, auto_delete, internal, arguments, scratches, policy, decorators]). +queue_decorators() -> + ok = queue_decorators(rabbit_queue), + ok = queue_decorators(rabbit_durable_queue). + +queue_decorators(Table) -> + transform( + Table, + fun ({amqqueue, Name, Durable, AutoDelete, ExclusiveOwner, Arguments, + Pid, SlavePids, SyncSlavePids, Policy, GmPids}) -> + {amqqueue, Name, Durable, AutoDelete, ExclusiveOwner, Arguments, + Pid, SlavePids, SyncSlavePids, Policy, GmPids, []} + end, + [name, durable, auto_delete, exclusive_owner, arguments, pid, slave_pids, + sync_slave_pids, policy, gm_pids, decorators]). %%-------------------------------------------------------------------- -- cgit v1.2.1 From 064d8ee7b5fd14dcc8fa21583dd0f8f39cca8fe5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 5 Jul 2013 17:26:02 +0100 Subject: Extend queue decorator interface, break dependency of server on federation. This is still a bit ugly, bits of queue state leak out in the events at the moment, and events are still exactly those needed by federation rather than a carefully thought out set. But it works. --- src/rabbit_amqqueue.erl | 8 +++--- src/rabbit_amqqueue_process.erl | 55 ++++++++++++++++++++++------------------- src/rabbit_queue_decorator.erl | 9 ++++++- 3 files changed, 41 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 5331a584..c5789f8a 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -26,7 +26,7 @@ -export([list/0, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). -export([force_event_refresh/0, wake_up/1]). -export([consumers/1, consumers_all/1, consumer_info_keys/0]). --export([basic_get/4, basic_consume/10, basic_cancel/4, notify_federation/1]). +-export([basic_get/4, basic_consume/10, basic_cancel/4, notify_decorators/1]). -export([notify_sent/2, notify_sent_queue_down/1, resume/2, flush_all/2]). -export([notify_down_all/2, activate_limit_all/2, credit/5]). -export([on_node_down/1]). @@ -156,7 +156,7 @@ -> rabbit_types:ok_or_error('exclusive_consume_unavailable')). -spec(basic_cancel/4 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok'). --spec(notify_federation/1 :: (rabbit_types:amqqueue()) -> 'ok'). +-spec(notify_decorators/1 :: (rabbit_types:amqqueue()) -> 'ok'). -spec(notify_sent/2 :: (pid(), pid()) -> 'ok'). -spec(notify_sent_queue_down/1 :: (pid()) -> 'ok'). -spec(resume/2 :: (pid(), pid()) -> 'ok'). @@ -569,8 +569,8 @@ basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, LimiterPid, LimiterActive, basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) -> delegate:call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). -notify_federation(#amqqueue{pid = QPid}) -> - delegate:cast(QPid, notify_federation). +notify_decorators(#amqqueue{pid = QPid}) -> + delegate:cast(QPid, notify_decorators). notify_sent(QPid, ChPid) -> Key = {consumer_credit_to, QPid}, diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 893659d4..6624b5d2 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -198,7 +198,7 @@ declare(Recover, From, State = #q{q = Q, recovery_barrier(Recover), State1 = process_args(State#q{backing_queue = BQ, backing_queue_state = BQS}), - rabbit_federation_queue:maybe_start(Q), + callback(qname(State), startup, []), rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State1)), rabbit_event:if_enabled(State1, #q.stats_timer, @@ -223,6 +223,22 @@ matches(new, Q1, Q2) -> matches(_, Q, Q) -> true; matches(_, _Q, _Q1) -> false. +callback(QName, F, A) -> + %% Look up again in case policy and hence decorators have changed + case rabbit_amqqueue:lookup(QName) of + {ok, Q = #amqqueue{decorators = Ds}} -> + [ok = apply(M, F, [Q|A]) || M <- rabbit_queue_decorator:select(Ds)]; + {error, not_found} -> + ok + end. + +notify_decorators(Event, Props, State = #q{active_consumers = ACs, + backing_queue = BQ, + backing_queue_state = BQS}) -> + callback(qname(State), notify, + [Event, [{active_consumers, ACs}, + {is_empty, BQ:is_empty(BQS)} | Props]]). + bq_init(BQ, Q, Recover) -> Self = self(), BQ:init(Q, Recover =/= new, @@ -265,7 +281,7 @@ init_dlx_routing_key(RoutingKey, State) -> init_max_length(MaxLen, State) -> State#q{max_length = MaxLen}. -terminate_shutdown(Fun, State = #q{q = Q}) -> +terminate_shutdown(Fun, State) -> State1 = #q{backing_queue_state = BQS} = lists:foldl(fun (F, S) -> F(S) end, State, [fun stop_sync_timer/1, @@ -276,7 +292,7 @@ terminate_shutdown(Fun, State = #q{q = Q}) -> undefined -> State1; _ -> ok = rabbit_memory_monitor:deregister(self()), QName = qname(State), - rabbit_federation_queue:maybe_stop(Q), + callback(QName, shutdown, []), [emit_consumer_deleted(Ch, CTag, QName) || {Ch, CTag, _} <- consumers(State1)], State1#q{backing_queue_state = Fun(BQS)} @@ -407,9 +423,10 @@ erase_ch_record(#cr{ch_pid = ChPid, monitor_ref = MonitorRef}) -> all_ch_record() -> [C || {{ch, _}, C} <- get()]. -block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry) -> +block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry, State) -> Blocked1 = priority_queue:in(QEntry, consumer_priority(QEntry), Blocked), - update_ch_record(C#cr{blocked_consumers = Blocked1}). + update_ch_record(C#cr{blocked_consumers = Blocked1}), + notify_decorators(consumer_blocked, [{q_entry, QEntry}], State). is_ch_blocked(#cr{unsent_message_count = Count, limiter = Limiter}) -> Count >= ?UNSENT_MESSAGE_LIMIT orelse rabbit_limiter:is_suspended(Limiter). @@ -446,15 +463,13 @@ deliver_msgs_to_consumers(DeliverFun, false, deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> C = lookup_ch(ChPid), case is_ch_blocked(C) of - true -> block_consumer(C, E), - notify_federation(State), + true -> block_consumer(C, E, State), {false, State}; false -> case rabbit_limiter:can_send(C#cr.limiter, Consumer#consumer.ack_required, Consumer#consumer.tag) of {suspend, Limiter} -> - block_consumer(C#cr{limiter = Limiter}, E), - notify_federation(State), + block_consumer(C#cr{limiter = Limiter}, E, State), {false, State}; {continue, Limiter} -> AC1 = priority_queue:in(E, consumer_priority(E), @@ -537,22 +552,9 @@ run_message_queue(State) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, is_empty(State), State), - notify_federation(State1), + notify_decorators(queue_finished, [], State1), State1. -notify_federation(#q{q = Q, - active_consumers = ActiveConsumers, - backing_queue = BQ, - backing_queue_state = BQS}) -> - IsEmpty = BQ:is_empty(BQS), - case IsEmpty andalso active_unfederated(ActiveConsumers) of - true -> rabbit_federation_queue:run(Q); - false -> rabbit_federation_queue:pause(Q) - end. - -active_unfederated(Cs) -> - not priority_queue:is_empty(Cs) andalso priority_queue:highest(Cs) >= 0. - consumer_priority({_ChPid, #consumer{args = Args}}) -> case rabbit_misc:table_lookup(Args, <<"x-priority">>) of {_, Priority} -> Priority; @@ -1227,7 +1229,8 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, _From, active_consumers = remove_consumer( ChPid, ConsumerTag, State#q.active_consumers)}, - notify_federation(State1), + notify_decorators( + basic_cancel, [{consumer_tag, ConsumerTag}], State1), case should_auto_delete(State1) of false -> reply(ok, ensure_expiry_timer(State1)); true -> stop(ok, State1) @@ -1404,8 +1407,8 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, end end); -handle_cast(notify_federation, State) -> - notify_federation(State), +handle_cast(notify_decorators, State) -> + notify_decorators(on_demand, [], State), noreply(State); handle_cast(wake_up, State) -> diff --git a/src/rabbit_queue_decorator.erl b/src/rabbit_queue_decorator.erl index 9ea13beb..d3dec9dc 100644 --- a/src/rabbit_queue_decorator.erl +++ b/src/rabbit_queue_decorator.erl @@ -8,17 +8,24 @@ -ifdef(use_specs). +-callback startup(rabbit_types:amqqueue()) -> 'ok'. + +-callback shutdown(rabbit_types:amqqueue()) -> 'ok'. + -callback policy_changed(rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'. -callback active_for(rabbit_types:amqqueue()) -> boolean(). +-callback notify(rabbit_types:amqqueue(), atom(), any()) -> 'ok'. + -else. -export([behaviour_info/1]). behaviour_info(callbacks) -> - [{description, 0}, {active_for, 1}, {policy_changed, 2}]; + [{description, 0}, {startup, 1}, {shutdown, 1}, {policy_changed, 2}, + {active_for, 1}, {notify, 3}]; behaviour_info(_Other) -> undefined. -- cgit v1.2.1 From b347d853f67574a192a071488bda46a241b8f84b Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 23 Jul 2013 15:20:55 +0400 Subject: Rename #throttle.conserve_resources to .alarmed_by --- src/rabbit_reader.erl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 7288d59a..0e88a65a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -45,7 +45,7 @@ client_properties, capabilities, auth_mechanism, auth_state}). --record(throttle, {conserve_resources, last_blocked_by, last_blocked_at, +-record(throttle, {alarmed_by, last_blocked_by, last_blocked_at, blocked_sent}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, @@ -248,10 +248,10 @@ start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, buf = [], buf_len = 0, throttle = #throttle{ - conserve_resources = [], - last_blocked_by = none, - last_blocked_at = never, - blocked_sent = false}}, + alarmed_by = [], + last_blocked_by = none, + last_blocked_at = never, + blocked_sent = false}}, try run({?MODULE, recvloop, [Deb, switch_callback(rabbit_event:init_stats_timer( @@ -326,12 +326,12 @@ mainloop(Deb, State = #v1{sock = Sock, buf = Buf, buf_len = BufLen}) -> handle_other({conserve_resources, Source, Conserve}, State = #v1{throttle = Throttle = - #throttle{conserve_resources = CR}}) -> + #throttle{alarmed_by = CR}}) -> CR1 = case Conserve of true -> lists:usort([Source | CR]); false -> CR -- [Source] end, - Throttle1 = Throttle#throttle{conserve_resources = CR1}, + Throttle1 = Throttle#throttle{alarmed_by = CR1}, control_throttle(State#v1{throttle = Throttle1}); handle_other({channel_closing, ChPid}, State) -> ok = rabbit_channel:ready_for_close(ChPid), @@ -417,7 +417,7 @@ terminate(_Explanation, State) -> {force, State}. control_throttle(State = #v1{connection_state = CS, throttle = Throttle}) -> - IsThrottled = ((Throttle#throttle.conserve_resources =/= []) orelse + IsThrottled = ((Throttle#throttle.alarmed_by =/= []) orelse credit_flow:blocked()), case {CS, IsThrottled} of {running, true} -> State#v1{connection_state = blocking}; @@ -444,9 +444,9 @@ maybe_block(State = #v1{connection_state = blocking, maybe_block(State) -> State. -maybe_send_blocked(#v1{throttle = #throttle{conserve_resources = []}}) -> +maybe_send_blocked(#v1{throttle = #throttle{alarmed_by = []}}) -> false; -maybe_send_blocked(#v1{throttle = #throttle{conserve_resources = CR}, +maybe_send_blocked(#v1{throttle = #throttle{alarmed_by = CR}, connection = #connection{ protocol = Protocol, capabilities = Capabilities}, @@ -468,7 +468,7 @@ maybe_send_unblocked(#v1{connection = #connection{protocol = Protocol}, sock = Sock}) -> ok = send_on_channel0(Sock, #'connection.unblocked'{}, Protocol). -update_last_blocked_by(Throttle = #throttle{conserve_resources = []}) -> +update_last_blocked_by(Throttle = #throttle{alarmed_by = []}) -> Throttle#throttle{last_blocked_by = flow}; update_last_blocked_by(Throttle) -> Throttle#throttle{last_blocked_by = resource}. @@ -883,7 +883,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, NewConnection = Connection#connection{vhost = VHostPath}, ok = send_on_channel0(Sock, #'connection.open_ok'{}, Protocol), Conserve = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}), - Throttle1 = Throttle#throttle{conserve_resources = Conserve}, + Throttle1 = Throttle#throttle{alarmed_by = Conserve}, {ok, ChannelSupSupPid} = supervisor2:start_child( ConnSupPid, -- cgit v1.2.1 From 82eae6e36d941af201aab2e49e1fc13002b8dccf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 23 Jul 2013 16:13:04 +0100 Subject: Wire in support for system messages. --- src/rabbit_writer.erl | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 2d15e6a2..3e966e16 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -19,6 +19,9 @@ -include("rabbit_framing.hrl"). -export([start/5, start_link/5, start/6, start_link/6]). + +-export([system_continue/3, system_terminate/4, system_code_change/4]). + -export([send_command/2, send_command/3, send_command_sync/2, send_command_sync/3, send_command_and_notify/4, send_command_and_notify/5, @@ -26,7 +29,7 @@ -export([internal_send_command/4, internal_send_command/6]). %% internal --export([mainloop/1, mainloop1/1]). +-export([mainloop/2, mainloop1/2]). -record(wstate, {sock, channel, frame_max, protocol, reader, stats_timer, pending}). @@ -53,6 +56,11 @@ (rabbit_net:socket(), rabbit_channel:channel_number(), non_neg_integer(), rabbit_types:protocol(), pid(), boolean()) -> rabbit_types:ok(pid())). + +-spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}). +-spec(system_continue/3 :: (_,_,#wstate{}) -> any()). +-spec(system_terminate/4 :: (_,_,_,_) -> none()). + -spec(send_command/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). -spec(send_command/3 :: @@ -94,12 +102,14 @@ start_link(Sock, Channel, FrameMax, Protocol, ReaderPid) -> start(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) -> State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats), - {ok, proc_lib:spawn(?MODULE, mainloop, [State])}. + Deb = sys:debug_options([]), + {ok, proc_lib:spawn(?MODULE, mainloop, [Deb, State])}. start_link(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) -> State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats), - {ok, proc_lib:spawn_link(?MODULE, mainloop, [State])}. + Deb = sys:debug_options([]), + {ok, proc_lib:spawn_link(?MODULE, mainloop, [Deb, State])}. initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) -> (case ReaderWantsStats of @@ -113,28 +123,44 @@ initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) -> pending = []}, #wstate.stats_timer). -mainloop(State) -> +system_continue(Parent, Deb, State) -> + ?MODULE:mainloop(Deb, State#wstate{reader = Parent}). + +system_terminate(Reason, _Parent, _Deb, _State) -> + exit(Reason). + +system_code_change(Misc, _Module, _OldVsn, _Extra) -> + {ok, Misc}. + +mainloop(Deb, State) -> try - mainloop1(State) + mainloop1(Deb, State) catch exit:Error -> #wstate{reader = ReaderPid, channel = Channel} = State, ReaderPid ! {channel_exit, Channel, Error} end, done. -mainloop1(State = #wstate{pending = []}) -> +mainloop1(Deb, State = #wstate{pending = []}) -> receive - Message -> ?MODULE:mainloop1(handle_message(Message, State)) + Message -> {Deb1, State1} = handle_message(Deb, Message, State), + ?MODULE:mainloop1(Deb1, State1) after ?HIBERNATE_AFTER -> erlang:hibernate(?MODULE, mainloop, [State]) end; -mainloop1(State) -> +mainloop1(Deb, State) -> receive - Message -> ?MODULE:mainloop1(handle_message(Message, State)) + Message -> {Deb1, State1} = handle_message(Deb, Message, State), + ?MODULE:mainloop1(Deb1, State1) after 0 -> - ?MODULE:mainloop1(internal_flush(State)) + ?MODULE:mainloop1(Deb, internal_flush(State)) end. +handle_message(Deb, {system, From, Req}, State = #wstate{reader = Parent}) -> + sys:handle_system_msg(Req, From, Parent, ?MODULE, Deb, State); +handle_message(Deb, Message, State) -> + {Deb, handle_message(Message, State)}. + handle_message({send_command, MethodRecord}, State) -> internal_send_command_async(MethodRecord, State); handle_message({send_command, MethodRecord, Content}, State) -> -- cgit v1.2.1 From ac1a06c4b144316b397f0ffb1582063fe0723e5e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 23 Jul 2013 17:57:50 +0100 Subject: Oops, missed one. --- src/rabbit_writer.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 3e966e16..78c117e8 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -146,7 +146,7 @@ mainloop1(Deb, State = #wstate{pending = []}) -> Message -> {Deb1, State1} = handle_message(Deb, Message, State), ?MODULE:mainloop1(Deb1, State1) after ?HIBERNATE_AFTER -> - erlang:hibernate(?MODULE, mainloop, [State]) + erlang:hibernate(?MODULE, mainloop, [Deb, State]) end; mainloop1(Deb, State) -> receive -- cgit v1.2.1 From 9a958054949dd064e1e5f1a7f1d0bfce9d91ec49 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Jul 2013 11:51:49 +0100 Subject: Support system messages in heartbeater. --- src/rabbit_heartbeat.erl | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl index e878f3bb..ca83206b 100644 --- a/src/rabbit_heartbeat.erl +++ b/src/rabbit_heartbeat.erl @@ -19,6 +19,11 @@ -export([start_heartbeat_sender/3, start_heartbeat_receiver/3, start_heartbeat_fun/1, pause_monitor/1, resume_monitor/1]). +-export([system_continue/3, system_terminate/4, system_code_change/4]). + +%% internal +-export([heartbeater/3]). + -include("rabbit.hrl"). %%---------------------------------------------------------------------------- @@ -51,6 +56,10 @@ -spec(pause_monitor/1 :: (heartbeaters()) -> 'ok'). -spec(resume_monitor/1 :: (heartbeaters()) -> 'ok'). +-spec(system_code_change/4 :: (_,_,_,_) -> {'ok',_}). +-spec(system_continue/3 :: (_,_,{_, _}) -> any()). +-spec(system_terminate/4 :: (_,_,_,_) -> none()). + -endif. %%---------------------------------------------------------------------------- @@ -88,6 +97,15 @@ pause_monitor({_Sender, Receiver}) -> Receiver ! pause, ok. resume_monitor({_Sender, none}) -> ok; resume_monitor({_Sender, Receiver}) -> Receiver ! resume, ok. +system_continue(_Parent, Deb, {Params, State}) -> + ?MODULE:heartbeater(Params, Deb, State). + +system_terminate(Reason, _Parent, _Deb, _State) -> + exit(Reason). + +system_code_change(Misc, _Module, _OldVsn, _Extra) -> + {ok, Misc}. + %%---------------------------------------------------------------------------- start_heartbeater(0, _SupPid, _Sock, _TimeoutFun, _Name, _Callback) -> {ok, none}; @@ -98,17 +116,27 @@ start_heartbeater(TimeoutSec, SupPid, Sock, TimeoutFun, Name, Callback) -> transient, ?MAX_WAIT, worker, [rabbit_heartbeat]}). heartbeater(Params) -> - {ok, proc_lib:spawn_link(fun () -> heartbeater(Params, {0, 0}) end)}. + Deb = sys:debug_options([]), + {ok, proc_lib:spawn_link(fun () -> heartbeater(Params, Deb, {0, 0}) end)}. heartbeater({Sock, TimeoutMillisec, StatName, Threshold, Handler} = Params, - {StatVal, SameCount}) -> - Recurse = fun (V) -> heartbeater(Params, V) end, + Deb, {StatVal, SameCount} = State) -> + Recurse = fun (State1) -> heartbeater(Params, Deb, State1) end, + System = fun (From, Req) -> + sys:handle_system_msg( + Req, From, self(), ?MODULE, Deb, {Params, State}) + end, receive - pause -> receive - resume -> Recurse({0, 0}); - Other -> exit({unexpected_message, Other}) - end; - Other -> exit({unexpected_message, Other}) + pause -> + receive + resume -> Recurse({0, 0}); + {system, From, Req} -> System(From, Req); + Other -> exit({unexpected_message, Other}) + end; + {system, From, Req} -> + System(From, Req); + Other -> + exit({unexpected_message, Other}) after TimeoutMillisec -> case rabbit_net:getstat(Sock, [StatName]) of {ok, [{StatName, NewStatVal}]} -> -- cgit v1.2.1 From a6cc463997862e19b81582e51b1a44c243dfbb0c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Jul 2013 14:05:33 +0100 Subject: Don't emit random bits of the queue's internal data structures in events. --- src/priority_queue.erl | 4 ++-- src/rabbit_amqqueue_process.erl | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 8ded389b..c92b6564 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -66,7 +66,7 @@ -spec(join/2 :: (pqueue(), pqueue()) -> pqueue()). -spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()). -spec(fold/3 :: (fun ((any(), any()) -> any()), any(), pqueue()) -> any()). --spec(highest/1 :: (pqueue()) -> priority()). +-spec(highest/1 :: (pqueue()) -> priority() | 'empty'). -endif. @@ -212,7 +212,7 @@ fold(Fun, Init, Q) -> case out_p(Q) of {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1) end. -highest({queue, [], [], 0}) -> exit(highest_priority_of_empty_queue); +highest({queue, [], [], 0}) -> empty; highest({queue, _, _, _}) -> 0; highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6624b5d2..136168f6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -236,8 +236,9 @@ notify_decorators(Event, Props, State = #q{active_consumers = ACs, backing_queue = BQ, backing_queue_state = BQS}) -> callback(qname(State), notify, - [Event, [{active_consumers, ACs}, - {is_empty, BQ:is_empty(BQS)} | Props]]). + [Event, + [{max_active_consumer_priority, priority_queue:highest(ACs)}, + {is_empty, BQ:is_empty(BQS)} | Props]]). bq_init(BQ, Q, Recover) -> Self = self(), @@ -423,10 +424,11 @@ erase_ch_record(#cr{ch_pid = ChPid, monitor_ref = MonitorRef}) -> all_ch_record() -> [C || {{ch, _}, C} <- get()]. -block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry, State) -> +block_consumer(C = #cr{blocked_consumers = Blocked}, + {_ChPid, #consumer{tag = CTag}} = QEntry, State) -> Blocked1 = priority_queue:in(QEntry, consumer_priority(QEntry), Blocked), update_ch_record(C#cr{blocked_consumers = Blocked1}), - notify_decorators(consumer_blocked, [{q_entry, QEntry}], State). + notify_decorators(consumer_blocked, [{consumer_tag, CTag}], State). is_ch_blocked(#cr{unsent_message_count = Count, limiter = Limiter}) -> Count >= ?UNSENT_MESSAGE_LIMIT orelse rabbit_limiter:is_suspended(Limiter). -- cgit v1.2.1 From 31675d9fc8b6947b88441580e0973ed45d76d715 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Jul 2013 14:07:13 +0100 Subject: Slightly clearer names? --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 136168f6..1244d640 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -554,7 +554,7 @@ run_message_queue(State) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, is_empty(State), State), - notify_decorators(queue_finished, [], State1), + notify_decorators(queue_run_finished, [], State1), State1. consumer_priority({_ChPid, #consumer{args = Args}}) -> @@ -1410,7 +1410,7 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, end); handle_cast(notify_decorators, State) -> - notify_decorators(on_demand, [], State), + notify_decorators(notification_requested, [], State), noreply(State); handle_cast(wake_up, State) -> -- cgit v1.2.1 From b214a650f31cfeb2d96b6a68541f98ebe68547d2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Jul 2013 14:28:55 +0100 Subject: More symmetry; notify on basic consume and consumer unblock as well as basic.cancel and consumer block. --- src/rabbit_amqqueue_process.erl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 1244d640..eeebae3f 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -695,7 +695,11 @@ unblock(State, C = #cr{limiter = Limiter}) -> UnblockedQ = priority_queue:from_list(Unblocked), update_ch_record(C#cr{blocked_consumers = BlockedQ}), AC1 = priority_queue:join(State#q.active_consumers, UnblockedQ), - run_message_queue(State#q{active_consumers = AC1}) + State1 = State#q{active_consumers = AC1}, + [notify_decorators( + consumer_unblocked, [{consumer_tag, CTag}], State1) || + {_P, {_ChPid, #consumer{tag = CTag}}} <- Unblocked], + run_message_queue(State1) end. should_auto_delete(#q{q = #amqqueue{auto_delete = false}}) -> false; @@ -1201,7 +1205,10 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, AC1 = priority_queue:in({ChPid, Consumer}, consumer_priority({ChPid, Consumer}), State1#q.active_consumers), - reply(ok, run_message_queue(State1#q{active_consumers = AC1})) + State2 = State1#q{active_consumers = AC1}, + notify_decorators( + basic_consume, [{consumer_tag, ConsumerTag}], State2), + reply(ok, run_message_queue(State2)) end; handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, _From, -- cgit v1.2.1 From 884f958ed89e26fe5632d170d5c522c532475bd9 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 24 Jul 2013 14:50:12 +0100 Subject: Write file using rename --- src/rabbit_file.erl | 53 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 3ceb4989..4412f04f 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -24,6 +24,8 @@ -export([rename/2, delete/1, recursive_delete/1, recursive_copy/2]). -export([lock_file/1]). +-define(TMP_EXT, ".tmp"). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -136,26 +138,43 @@ write_term_file(File, Terms) -> write_file(Path, Data) -> write_file(Path, Data, []). -%% write_file/3 and make_binary/1 are both based on corresponding -%% functions in the kernel/file.erl module of the Erlang R14B02 -%% release, which is licensed under the EPL. That implementation of -%% write_file/3 does not do an fsync prior to closing the file, hence -%% the existence of this version. APIs are otherwise identical. write_file(Path, Data, Modes) -> Modes1 = [binary, write | (Modes -- [binary, write])], case make_binary(Data) of - Bin when is_binary(Bin) -> - with_fhc_handle( - fun () -> case prim_file:open(Path, Modes1) of - {ok, Hdl} -> try prim_file:write(Hdl, Bin) of - ok -> prim_file:sync(Hdl); - {error, _} = E -> E - after - prim_file:close(Hdl) - end; - {error, _} = E -> E - end - end); + Bin when is_binary(Bin) -> write_file1(Path, Bin, Modes1); + {error, _} = E -> E + end. + +write_file1(Path, Bin, Modes) -> + try + with_copy(Path, + fun (Copy) -> + with_sync(Copy, Modes, + fun (CopyHdl) -> + ok = prim_file:write(CopyHdl, Bin) + end) + end) + catch + error:{badmatch, Error} -> Error; + _:{error, Error} -> {error, Error} + end. + +with_copy(Path, Fun) -> + Bak = Path ++ ?TMP_EXT, + Result = Fun(Bak), + file:rename(Bak, Path), + ok = with_sync(Path, [binary], fun (_Hdl) -> ok end), + Result. + +with_sync(Path, Modes, Fun) -> + case prim_file:open(Path, Modes) of + {ok, Hdl} -> try + Result = Fun(Hdl), + ok = prim_file:sync(Hdl), + Result + after + prim_file:close(Hdl) + end; {error, _} = E -> E end. -- cgit v1.2.1 From 1bbc5600b2e0059d409cbd745443ebede701732f Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 24 Jul 2013 18:18:01 +0100 Subject: Avoid use of 'file' module in rabbit_file --- src/rabbit_file.erl | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 4412f04f..7f0add2b 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -147,37 +147,42 @@ write_file(Path, Data, Modes) -> write_file1(Path, Bin, Modes) -> try - with_copy(Path, - fun (Copy) -> - with_sync(Copy, Modes, - fun (CopyHdl) -> - ok = prim_file:write(CopyHdl, Bin) - end) - end) + with_synced_copy(Path, Modes, + fun (Hdl) -> + ok = prim_file:write(Hdl, Bin) + end) catch error:{badmatch, Error} -> Error; _:{error, Error} -> {error, Error} end. -with_copy(Path, Fun) -> +with_synced_copy(Path, Modes, Fun) -> Bak = Path ++ ?TMP_EXT, - Result = Fun(Bak), - file:rename(Bak, Path), - ok = with_sync(Path, [binary], fun (_Hdl) -> ok end), - Result. - -with_sync(Path, Modes, Fun) -> - case prim_file:open(Path, Modes) of - {ok, Hdl} -> try - Result = Fun(Hdl), - ok = prim_file:sync(Hdl), - Result - after - prim_file:close(Hdl) - end; - {error, _} = E -> E + case lists:member(append, Modes) of + true -> + {error, append_not_supported, Path}; + false -> + with_fhc_handle( + fun () -> + case prim_file:open(Bak, Modes) of + {ok, Hdl} -> + try + Result = Fun(Hdl), + ok = prim_file:rename(Bak, Path), + ok = prim_file:sync(Hdl), + Result + after + prim_file:close(Hdl) + end; + {error, _} = E -> E + end + end) end. +%% make_binary/1 is based on the corresponding function in the +%% kernel/file.erl module of the Erlang R14B02 release, which is +%% licensed under the EPL. + make_binary(Bin) when is_binary(Bin) -> Bin; make_binary(List) -> -- cgit v1.2.1 From 6928e7a1063c657b4ffacd0631a32f66d5a2960a Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 25 Jul 2013 12:21:22 +0100 Subject: Re-order --- src/rabbit_file.erl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 7f0add2b..93b9c901 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -145,6 +145,19 @@ write_file(Path, Data, Modes) -> {error, _} = E -> E end. +%% make_binary/1 is based on the corresponding function in the +%% kernel/file.erl module of the Erlang R14B02 release, which is +%% licensed under the EPL. + +make_binary(Bin) when is_binary(Bin) -> + Bin; +make_binary(List) -> + try + iolist_to_binary(List) + catch error:Reason -> + {error, Reason} + end. + write_file1(Path, Bin, Modes) -> try with_synced_copy(Path, Modes, @@ -157,13 +170,13 @@ write_file1(Path, Bin, Modes) -> end. with_synced_copy(Path, Modes, Fun) -> - Bak = Path ++ ?TMP_EXT, case lists:member(append, Modes) of true -> {error, append_not_supported, Path}; false -> with_fhc_handle( fun () -> + Bak = Path ++ ?TMP_EXT, case prim_file:open(Bak, Modes) of {ok, Hdl} -> try @@ -179,19 +192,6 @@ with_synced_copy(Path, Modes, Fun) -> end) end. -%% make_binary/1 is based on the corresponding function in the -%% kernel/file.erl module of the Erlang R14B02 release, which is -%% licensed under the EPL. - -make_binary(Bin) when is_binary(Bin) -> - Bin; -make_binary(List) -> - try - iolist_to_binary(List) - catch error:Reason -> - {error, Reason} - end. - %% TODO the semantics of this function are rather odd. But see bug 25021. append_file(File, Suffix) -> case read_file_info(File) of -- cgit v1.2.1 From ca0662bf60a110b7144edb66f13ea588ba4bbb52 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 30 Jul 2013 13:12:26 +0100 Subject: Untabify --- src/rabbit_alarm.erl | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 40be1951..063e98aa 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -106,22 +106,22 @@ handle_call(_Request, State) -> handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> IsDuplicate = lists:member(Alarm, Alarms), case IsDuplicate of - true -> - {ok, State}; - false -> - UpdatedAlarms = lists:usort([Alarm|Alarms]), - handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}) + true -> + {ok, State}; + false -> + UpdatedAlarms = lists:usort([Alarm|Alarms]), + handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}) end; handle_event({clear_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> ExistingAlarm = lists:keymember(Alarm, 1, Alarms), case ExistingAlarm of - true -> - handle_clear_alarm(Alarm, - State#alarms{alarms = lists:keydelete(Alarm, 1, - Alarms)}); - false -> - {ok, State} + true -> + handle_clear_alarm(Alarm, + State#alarms{alarms = lists:keydelete(Alarm, 1, + Alarms)}); + false -> + {ok, State} end; @@ -158,9 +158,9 @@ code_change(_OldVsn, State, _Extra) -> dict_append(Key, Val, Dict) -> L = case dict:find(Key, Dict) of - {ok, V} -> V; - error -> [] - end, + {ok, V} -> V; + error -> [] + end, dict:store(Key, lists:usort([Val|L]), Dict). dict_unappend_all(Key, _Val, Dict) -> @@ -168,9 +168,9 @@ dict_unappend_all(Key, _Val, Dict) -> dict_unappend(Key, Val, Dict) -> L = case dict:find(Key, Dict) of - {ok, V} -> V; - error -> [] - end, + {ok, V} -> V; + error -> [] + end, case lists:delete(Val, L) of [] -> dict:erase(Key, Dict); @@ -192,10 +192,10 @@ maybe_alert(UpdateFun, Node, Source, Event, ok end, case Event of - clear -> - ok = alert_local(false, Alertees, Source); - set -> - ok = alert_local(true, Alertees, Source) + clear -> + ok = alert_local(false, Alertees, Source); + set -> + ok = alert_local(true, Alertees, Source) end, State#alarms{alarmed_nodes = AN1}. -- cgit v1.2.1 From 41e6279f391681058f7bed93d6f451636dac3de0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 30 Jul 2013 13:23:49 +0100 Subject: Inlining and cosmetic. --- src/rabbit_alarm.erl | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 063e98aa..c8747c9a 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -104,24 +104,18 @@ handle_call(_Request, State) -> {ok, not_understood, State}. handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> - IsDuplicate = lists:member(Alarm, Alarms), - case IsDuplicate of - true -> - {ok, State}; - false -> - UpdatedAlarms = lists:usort([Alarm|Alarms]), - handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}) + case lists:member(Alarm, Alarms) of + true -> {ok, State}; + false -> UpdatedAlarms = lists:usort([Alarm|Alarms]), + handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms}) end; handle_event({clear_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> - ExistingAlarm = lists:keymember(Alarm, 1, Alarms), - case ExistingAlarm of - true -> - handle_clear_alarm(Alarm, - State#alarms{alarms = lists:keydelete(Alarm, 1, - Alarms)}); - false -> - {ok, State} + case lists:keymember(Alarm, 1, Alarms) of + true -> handle_clear_alarm( + Alarm, State#alarms{alarms = lists:keydelete( + Alarm, 1, Alarms)}); + false -> {ok, State} end; @@ -192,10 +186,8 @@ maybe_alert(UpdateFun, Node, Source, Event, ok end, case Event of - clear -> - ok = alert_local(false, Alertees, Source); - set -> - ok = alert_local(true, Alertees, Source) + clear -> ok = alert_local(false, Alertees, Source); + set -> ok = alert_local(true, Alertees, Source) end, State#alarms{alarmed_nodes = AN1}. -- cgit v1.2.1 From 56b47f4015bc99b50608c8ca472de6a0aea68f6b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 30 Jul 2013 13:29:45 +0100 Subject: Convert Event set / clear to Alert true / false since that's how it is everywhere else. Further simplify. Remove a no longer true comment. --- src/rabbit_alarm.erl | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index c8747c9a..08b34820 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -127,7 +127,7 @@ handle_event({node_up, Node}, State) -> {ok, State}; handle_event({node_down, Node}, State) -> - {ok, maybe_alert(fun dict_unappend_all/3, Node, [], clear, State)}; + {ok, maybe_alert(fun dict_unappend_all/3, Node, [], false, State)}; handle_event({register, Pid, AlertMFA}, State) -> {ok, internal_register(Pid, AlertMFA, State)}; @@ -171,24 +171,15 @@ dict_unappend(Key, Val, Dict) -> X -> dict:store(Key, X, Dict) end. -maybe_alert(UpdateFun, Node, Source, Event, +maybe_alert(UpdateFun, Node, Source, Alert, State = #alarms{alarmed_nodes = AN, alertees = Alertees}) -> AN1 = UpdateFun(Node, Source, AN), - - %% If we have changed our alarm state, inform the remotes. - IsLocal = Node =:= node(), - if IsLocal andalso Event =:= set -> - ok = alert_remote(true, Alertees, Source); - IsLocal andalso Event =:= clear -> - ok = alert_remote(false, Alertees, Source); - true -> - ok - end, - case Event of - clear -> ok = alert_local(false, Alertees, Source); - set -> ok = alert_local(true, Alertees, Source) + case node() of + Node -> ok = alert_remote(Alert, Alertees, Source); + _ -> ok end, + ok = alert_local(Alert, Alertees, Source), State#alarms{alarmed_nodes = AN1}. alert_local(Alert, Alertees, Source) -> @@ -223,7 +214,7 @@ handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> "*** Publishers will be blocked until this alarm clears ***~n" "**********************************************************~n", [Source, Node]), - {ok, maybe_alert(fun dict_append/3, Node, Source, set, State)}; + {ok, maybe_alert(fun dict_append/3, Node, Source, true, State)}; handle_set_alarm({file_descriptor_limit, []}, State) -> rabbit_log:warning( "file descriptor limit alarm set.~n~n" @@ -238,7 +229,7 @@ handle_set_alarm(Alarm, State) -> handle_clear_alarm({resource_limit, Source, Node}, State) -> rabbit_log:warning("~s resource limit alarm cleared on node ~p~n", [Source, Node]), - {ok, maybe_alert(fun dict_unappend/3, Node, Source, clear, State)}; + {ok, maybe_alert(fun dict_unappend/3, Node, Source, false, State)}; handle_clear_alarm(file_descriptor_limit, State) -> rabbit_log:warning("file descriptor limit alarm cleared~n"), {ok, State}; -- cgit v1.2.1 From d32815323a0d72bbf05c6662cbe5d96612dc9a68 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 30 Jul 2013 16:46:46 +0100 Subject: Pick an apply-to value heuristically. --- src/rabbit_upgrade_functions.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index d76002dd..76bfe28a 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -305,12 +305,22 @@ policy_apply_to() -> transform( rabbit_runtime_parameters, fun ({runtime_parameters, Key = {_VHost, <<"policy">>, _Name}, Value}) -> - {runtime_parameters, Key, [{<<"apply-to">>, <<"both">>} | Value]}; + ApplyTo = apply_to(proplists:get_value(<<"definition">>, Value)), + {runtime_parameters, Key, [{<<"apply-to">>, ApplyTo} | Value]}; ({runtime_parameters, Key, Value}) -> {runtime_parameters, Key, Value} end, [key, value]). +apply_to(Def) -> + case [proplists:get_value(K, Def) || + K <- [<<"federation-upstream-set">>, <<"ha-mode">>]] of + [undefined, undefined] -> <<"both">>; + [_, undefined] -> <<"exchanges">>; + [undefined, _] -> <<"queues">>; + [_, _] -> <<"both">> + end. + %%-------------------------------------------------------------------- transform(TableName, Fun, FieldList) -> -- cgit v1.2.1 From b688b4ca7e6f0720241945920676160603705375 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 30 Jul 2013 17:57:46 +0100 Subject: Remove redundancy. --- src/rabbit_heartbeat.erl | 5 +---- src/rabbit_reader.erl | 2 +- src/rabbit_writer.erl | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl index ca83206b..82cccea9 100644 --- a/src/rabbit_heartbeat.erl +++ b/src/rabbit_heartbeat.erl @@ -21,9 +21,6 @@ -export([system_continue/3, system_terminate/4, system_code_change/4]). -%% internal --export([heartbeater/3]). - -include("rabbit.hrl"). %%---------------------------------------------------------------------------- @@ -98,7 +95,7 @@ resume_monitor({_Sender, none}) -> ok; resume_monitor({_Sender, Receiver}) -> Receiver ! resume, ok. system_continue(_Parent, Deb, {Params, State}) -> - ?MODULE:heartbeater(Params, Deb, State). + heartbeater(Params, Deb, State). system_terminate(Reason, _Parent, _Deb, _State) -> exit(Reason). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 3cf88d06..b0300ef5 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -120,7 +120,7 @@ init(Parent, ChSup3Pid, Collector, StartHeartbeatFun) -> end. system_continue(Parent, Deb, State) -> - ?MODULE:mainloop(Deb, State#v1{parent = Parent}). + mainloop(Deb, State#v1{parent = Parent}). system_terminate(Reason, _Parent, _Deb, _State) -> exit(Reason). diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 78c117e8..1b849fed 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -124,7 +124,7 @@ initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) -> #wstate.stats_timer). system_continue(Parent, Deb, State) -> - ?MODULE:mainloop(Deb, State#wstate{reader = Parent}). + mainloop(Deb, State#wstate{reader = Parent}). system_terminate(Reason, _Parent, _Deb, _State) -> exit(Reason). -- cgit v1.2.1 From 09d08b56849b093a6aae278a6c20a0bec3fcdabd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 Aug 2013 13:32:24 +0100 Subject: Reevaluate reified policies on upgrade. --- src/rabbit.erl | 1 + src/rabbit_policy.erl | 45 ++++++++++++++++++++++++++++++++++++++-- src/rabbit_upgrade_functions.erl | 4 +++- 3 files changed, 47 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/rabbit.erl b/src/rabbit.erl index dddf6f47..569c5385 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -581,6 +581,7 @@ boot_delegate() -> rabbit_sup:start_supervisor_child(delegate_sup, [Count]). recover() -> + rabbit_policy:recover(), Qs = rabbit_amqqueue:recover(), ok = rabbit_binding:recover(rabbit_exchange:recover(), [QName || #amqqueue{name = QName} <- Qs]), diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 4c5323f9..bafb017b 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -25,6 +25,7 @@ -import(rabbit_misc, [pget/2]). -export([register/0]). +-export([invalidate/0, recover/0]). -export([name/1, get/2, set/1]). -export([validate/4, notify/4, notify_clear/3]). -export([parse_set/5, set/6, delete/2, lookup/2, list/0, list/1, @@ -51,6 +52,10 @@ set(X = #exchange{name = Name}) -> rabbit_exchange_decorator:set( set0(Name = #resource{virtual_host = VHost}) -> match(Name, list(VHost)). +set(Q = #amqqueue{name = Name}, Ps) -> Q#amqqueue{policy = match(Name, Ps)}; +set(X = #exchange{name = Name}, Ps) -> rabbit_exchange_decorator:set( + X#exchange{policy = match(Name, Ps)}). + get(Name, #amqqueue{policy = Policy}) -> get0(Name, Policy); get(Name, #exchange{policy = Policy}) -> get0(Name, Policy); %% Caution - SLOW. @@ -68,6 +73,41 @@ get0(Name, List) -> case pget(definition, List) of %%---------------------------------------------------------------------------- +%% Gets called during upgrades - therefore must not assume anything about the +%% state of Mnesia +invalidate() -> + rabbit_file:write_file(invalid_file(), <<"">>). + +recover() -> + case rabbit_file:is_file(invalid_file()) of + true -> recover0(), + rabbit_file:delete(invalid_file()); + false -> ok + end. + +%% To get here we have to have just completed an Mnesia upgrade - i.e. we are +%% the first node starting. So we can rewrite the whole database. Note that +%% recovery has not yet happened; we must work with the rabbit_durable_ +%% variants. +recover0() -> + Xs = mnesia:dirty_match_object(rabbit_durable_exchange, #exchange{_ = '_'}), + Qs = mnesia:dirty_match_object(rabbit_durable_queue, #amqqueue{_ = '_'}), + Policies = list(), + [rabbit_misc:execute_mnesia_transaction( + fun () -> + mnesia:write(rabbit_durable_exchange, set(X, Policies), write) + end) || X <- Xs], + [rabbit_misc:execute_mnesia_transaction( + fun () -> + mnesia:write(rabbit_durable_queue, set(Q, Policies), write) + end) || Q <- Qs], + ok. + +invalid_file() -> + filename:join(rabbit_mnesia:dir(), "policies_are_invalid"). + +%%---------------------------------------------------------------------------- + parse_set(VHost, Name, Pattern, Definition, undefined) -> parse_set0(VHost, Name, Pattern, Definition, 0); parse_set(VHost, Name, Pattern, Definition, Priority) -> @@ -211,9 +251,10 @@ match(Name, Policies) -> [Policy | _Rest] -> Policy end. -matches(#resource{name = Name, kind = Kind}, Policy) -> +matches(#resource{name = Name, kind = Kind, virtual_host = VHost}, Policy) -> matches_type(Kind, pget('apply-to', Policy)) andalso - match =:= re:run(Name, pget(pattern, Policy), [{capture, none}]). + match =:= re:run(Name, pget(pattern, Policy), [{capture, none}]) andalso + VHost =:= pget(vhost, Policy). matches_type(exchange, <<"exchanges">>) -> true; matches_type(queue, <<"queues">>) -> true; diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 76bfe28a..b9ba4d76 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -310,7 +310,9 @@ policy_apply_to() -> ({runtime_parameters, Key, Value}) -> {runtime_parameters, Key, Value} end, - [key, value]). + [key, value]), + rabbit_policy:invalidate(), + ok. apply_to(Def) -> case [proplists:get_value(K, Def) || -- cgit v1.2.1 From 104facf19fd340bfb4d14783460afb9fb8d4f8a8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Aug 2013 17:00:19 +0100 Subject: "all" is clearer than "both". --- src/rabbit_policy.erl | 10 +++++----- src/rabbit_upgrade_functions.erl | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index bafb017b..b28fbb56 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -143,7 +143,7 @@ set(VHost, Name, Pattern, Definition, Priority, ApplyTo) -> set0(VHost, Name, Term0) -> Term = case pget(<<"apply-to">>, Term0) of - undefined -> [{<<"apply-to">>, <<"both">>} | Term0]; + undefined -> [{<<"apply-to">>, <<"all">>} | Term0]; _ -> Term0 end, rabbit_runtime_parameters:set_any(VHost, <<"policy">>, Name, Term). @@ -258,8 +258,8 @@ matches(#resource{name = Name, kind = Kind, virtual_host = VHost}, Policy) -> matches_type(exchange, <<"exchanges">>) -> true; matches_type(queue, <<"queues">>) -> true; -matches_type(exchange, <<"both">>) -> true; -matches_type(queue, <<"both">>) -> true; +matches_type(exchange, <<"all">>) -> true; +matches_type(queue, <<"all">>) -> true; matches_type(_, _) -> false. sort_pred(A, B) -> pget(priority, A) >= pget(priority, B). @@ -316,9 +316,9 @@ dups(L) -> L -- lists:usort(L). is_proplist(L) -> length(L) =:= length([I || I = {_, _} <- L]). -apply_to_validation(_Name, <<"both">>) -> ok; +apply_to_validation(_Name, <<"all">>) -> ok; apply_to_validation(_Name, <<"exchanges">>) -> ok; apply_to_validation(_Name, <<"queues">>) -> ok; apply_to_validation(_Name, Term) -> {error, "apply-to '~s' unrecognised; should be 'queues', 'exchanges' " - "or 'both'", [Term]}. + "or 'all'", [Term]}. diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index b9ba4d76..e28d4d52 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -317,10 +317,10 @@ policy_apply_to() -> apply_to(Def) -> case [proplists:get_value(K, Def) || K <- [<<"federation-upstream-set">>, <<"ha-mode">>]] of - [undefined, undefined] -> <<"both">>; + [undefined, undefined] -> <<"all">>; [_, undefined] -> <<"exchanges">>; [undefined, _] -> <<"queues">>; - [_, _] -> <<"both">> + [_, _] -> <<"all">> end. %%-------------------------------------------------------------------- -- cgit v1.2.1 From 7f8d837f75a07900d41086e405644de1d01cd811 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Aug 2013 17:05:15 +0100 Subject: Show apply-to in rabbitmqctl list_policies. --- src/rabbit_policy.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index b28fbb56..c2311f18 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -188,7 +188,7 @@ format(Term) -> ident(X) -> X. -info_keys() -> [vhost, name, pattern, definition, priority]. +info_keys() -> [vhost, name, 'apply-to', pattern, definition, priority]. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 4c1b5676a93b83c0058aecf6c93049914673e7f2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Aug 2013 17:07:36 +0100 Subject: Gross stupidity. --- src/rabbit_policy.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index c2311f18..5b2e2da2 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -136,7 +136,7 @@ set(VHost, Name, Pattern, Definition, Priority, ApplyTo) -> _ -> Priority end}, {<<"apply-to">>, case ApplyTo of - undefined -> 0; + undefined -> <<"all">>; _ -> ApplyTo end}], set0(VHost, Name, PolicyProps). -- cgit v1.2.1 From 734b2d9c2feee3f90cd69f19ba45bfbac065143e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 6 Aug 2013 16:45:33 +0100 Subject: Delay clearing of state in slaves until sender down notification is received from channel as well as GM in order to avoid messages being enqueued more than once --- src/rabbit_mirror_queue_coordinator.erl | 27 ++++++------ src/rabbit_mirror_queue_slave.erl | 78 +++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index c9918fed..f54e9bd1 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -222,20 +222,19 @@ %% sender_death message to all the slaves, saying the sender has %% died. Once the slaves receive the sender_death message, they know %% that they're not going to receive any more instructions from the gm -%% regarding that sender, thus they throw away any publications from -%% the sender pending publication instructions. However, it is -%% possible that the coordinator receives the DOWN and communicates -%% that to the master before the master has finished receiving and -%% processing publishes from the sender. This turns out not to be a -%% problem: the sender has actually died, and so will not need to -%% receive confirms or other feedback, and should further messages be -%% "received" from the sender, the master will ask the coordinator to -%% set up a new monitor, and will continue to process the messages -%% normally. Slaves may thus receive publishes via gm from previously -%% declared "dead" senders, but again, this is fine: should the slave -%% have just thrown out the message it had received directly from the -%% sender (due to receiving a sender_death message via gm), it will be -%% able to cope with the publication purely from the master via gm. +%% regarding that sender. However, it is possible that the coordinator +%% receives the DOWN and communicates that to the master before the +%% master has finished receiving and processing publishes from the +%% sender. This turns out not to be a problem: the sender has actually +%% died, and so will not need to receive confirms or other feedback, +%% and should further messages be "received" from the sender, the +%% master will ask the coordinator to set up a new monitor, and +%% will continue to process the messages normally. Slaves may thus +%% receive publishes via gm from previously declared "dead" senders, +%% but again, this is fine: should the slave have just thrown out the +%% message it had received directly from the sender (due to receiving +%% a sender_death message via gm), it will be able to cope with the +%% publication purely from the master via gm. %% %% When a slave receives a DOWN message for a sender, if it has not %% received the sender_death message from the master via gm already, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1996fd0a..6425a855 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -61,7 +61,7 @@ sync_timer_ref, rate_timer_ref, - sender_queues, %% :: Pid -> {Q Msg, Set MsgId} + sender_queues, %% :: Pid -> {Q Msg, Set MsgId, ChState} msg_id_ack, %% :: MsgId -> AckTag msg_id_status, @@ -275,7 +275,7 @@ handle_info({'DOWN', _MonitorRef, process, MPid, _Reason}, handle_info({'DOWN', _MonitorRef, process, ChPid, _Reason}, State) -> local_sender_death(ChPid, State), - noreply(State); + noreply(sender_lifetime(ChPid, down_from_ch, State)); handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; @@ -563,10 +563,15 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, (_Msgid, _Status, MTC0) -> MTC0 end, gb_trees:empty(), MS), - Deliveries = [Delivery || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), - Delivery <- queue:to_list(PubQ)], + Deliveries = [Delivery || + {_ChPid, {PubQ, _PendCh, _ChState}} <- dict:to_list(SQ), + Delivery <- queue:to_list(PubQ)], + AwaitGmDown = [ChPid || {ChPid, {_, _, down_from_ch}} <- dict:to_list(SQ)], + KS1 = lists:foldl(fun (ChPid0, KS0) -> + pmon:demonitor(ChPid0, KS0) + end, KS, AwaitGmDown), rabbit_amqqueue_process:init_with_backing_queue_state( - Q1, rabbit_mirror_queue_master, MasterState, RateTRef, Deliveries, KS, + Q1, rabbit_mirror_queue_master, MasterState, RateTRef, Deliveries, KS1, MTC). noreply(State) -> @@ -643,12 +648,39 @@ confirm_sender_death(Pid) -> State end, %% Note that we do not remove our knowledge of this ChPid until we - %% get the sender_death from GM. + %% get the sender_death from GM as well as a DOWN notification. {ok, _TRef} = timer:apply_after( ?DEATH_TIMEOUT, rabbit_amqqueue, run_backing_queue, [self(), rabbit_mirror_queue_master, Fun]), ok. +forget_sender(running, _) -> false; +forget_sender(_, running) -> false; +forget_sender(Down1, Down2) when Down1 =/= Down2 -> true. + +%% Record and process lifetime events from channels. Forget all about a channel +%% only when down notifications are received from both the channel and from gm. +sender_lifetime(ChPid, ChState, State = #state { sender_queues = SQ, + msg_id_status = MS, + known_senders = KS }) -> + case dict:find(ChPid, SQ) of + error -> + State; + {ok, {MQ, PendCh, ChStateRecord}} -> + case forget_sender(ChState, ChStateRecord) of + true -> + credit_flow:peer_down(ChPid), + State #state { sender_queues = dict:erase(ChPid, SQ), + msg_id_status = lists:foldl( + fun dict:erase/2, + MS, sets:to_list(PendCh)), + known_senders = pmon:demonitor(ChPid, KS) }; + false -> + SQ1 = dict:store(ChPid, {MQ, PendCh, ChState}, SQ), + State #state { sender_queues = SQ1 } + end + end. + maybe_enqueue_message( Delivery = #delivery { message = #basic_message { id = MsgId }, sender = ChPid }, @@ -657,9 +689,9 @@ maybe_enqueue_message( %% We will never see {published, ChPid, MsgSeqNo} here. case dict:find(MsgId, MS) of error -> - {MQ, PendingCh} = get_sender_queue(ChPid, SQ), + {MQ, PendingCh, ChState} = get_sender_queue(ChPid, SQ), MQ1 = queue:in(Delivery, MQ), - SQ1 = dict:store(ChPid, {MQ1, PendingCh}, SQ), + SQ1 = dict:store(ChPid, {MQ1, PendingCh, ChState}, SQ), State1 #state { sender_queues = SQ1 }; {ok, Status} -> MS1 = send_or_record_confirm( @@ -671,7 +703,7 @@ maybe_enqueue_message( get_sender_queue(ChPid, SQ) -> case dict:find(ChPid, SQ) of - error -> {queue:new(), sets:new()}; + error -> {queue:new(), sets:new(), running}; {ok, Val} -> Val end. @@ -679,19 +711,20 @@ remove_from_pending_ch(MsgId, ChPid, SQ) -> case dict:find(ChPid, SQ) of error -> SQ; - {ok, {MQ, PendingCh}} -> - dict:store(ChPid, {MQ, sets:del_element(MsgId, PendingCh)}, SQ) + {ok, {MQ, PendingCh, ChState}} -> + dict:store(ChPid, {MQ, sets:del_element(MsgId, PendingCh), ChState}, + SQ) end. publish_or_discard(Status, ChPid, MsgId, State = #state { sender_queues = SQ, msg_id_status = MS }) -> %% We really are going to do the publish/discard right now, even %% though we may not have seen it directly from the channel. But - %% we cannot issues confirms until the latter has happened. So we + %% we cannot issue confirms until the latter has happened. So we %% need to keep track of the MsgId and its confirmation status in %% the meantime. State1 = ensure_monitoring(ChPid, State), - {MQ, PendingCh} = get_sender_queue(ChPid, SQ), + {MQ, PendingCh, ChState} = get_sender_queue(ChPid, SQ), {MQ1, PendingCh1, MS1} = case queue:out(MQ) of {empty, _MQ2} -> @@ -711,7 +744,7 @@ publish_or_discard(Status, ChPid, MsgId, %% expecting any confirms from us. {MQ, PendingCh, MS} end, - SQ1 = dict:store(ChPid, {MQ1, PendingCh1}, SQ), + SQ1 = dict:store(ChPid, {MQ1, PendingCh1, ChState}, SQ), State1 #state { sender_queues = SQ1, msg_id_status = MS1 }. @@ -772,25 +805,14 @@ process_instruction({requeue, MsgIds}, {ok, State #state { msg_id_ack = MA1, backing_queue_state = BQS1 }}; process_instruction({sender_death, ChPid}, - State = #state { sender_queues = SQ, - msg_id_status = MS, - known_senders = KS }) -> + State = #state { known_senders = KS }) -> %% The channel will be monitored iff we have received a message %% from it. In this case we just want to avoid doing work if we %% never got any messages. {ok, case pmon:is_monitored(ChPid, KS) of false -> State; - true -> MS1 = case dict:find(ChPid, SQ) of - error -> - MS; - {ok, {_MQ, PendingCh}} -> - lists:foldl(fun dict:erase/2, MS, - sets:to_list(PendingCh)) - end, - credit_flow:peer_down(ChPid), - State #state { sender_queues = dict:erase(ChPid, SQ), - msg_id_status = MS1, - known_senders = pmon:demonitor(ChPid, KS) } + true -> credit_flow:peer_down(ChPid), + sender_lifetime(ChPid, down_from_gm, State) end}; process_instruction({depth, Depth}, State = #state { backing_queue = BQ, -- cgit v1.2.1 From 96b1c38adb4aa3a0b24922bf384d9140b19a215f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Aug 2013 14:56:31 +0100 Subject: Support MFA in delegate:invoke/2 and friends. --- src/delegate.erl | 73 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 7a06c1e4..9a1f6886 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -33,15 +33,14 @@ -export_type([monitor_ref/0]). -type(monitor_ref() :: reference() | {atom(), pid()}). +-type(fun_or_mfa() :: fun ((pid()) -> any()) | {atom(), atom(), [any()]}). -spec(start_link/1 :: (non_neg_integer()) -> {'ok', pid()} | ignore | {'error', any()}). --spec(invoke/2 :: - ( pid(), fun ((pid()) -> A)) -> A; - ([pid()], fun ((pid()) -> A)) -> {[{pid(), A}], - [{pid(), term()}]}). --spec(invoke_no_result/2 :: - (pid() | [pid()], fun ((pid()) -> any())) -> 'ok'). +-spec(invoke/2 :: ( pid(), fun_or_mfa()) -> any(); + ([pid()], fun_or_mfa()) -> {[{pid(), any()}], + [{pid(), term()}]}). +-spec(invoke_no_result/2 :: (pid() | [pid()], fun_or_mfa()) -> 'ok'). -spec(monitor/2 :: ('process', pid()) -> monitor_ref()). -spec(demonitor/1 :: (monitor_ref()) -> 'true'). -spec(demonitor/2 :: (monitor_ref(), ['flush']) -> 'true'). @@ -64,24 +63,24 @@ start_link(Num) -> Name = delegate_name(Num), gen_server2:start_link({local, Name}, ?MODULE, [Name], []). -invoke(Pid, Fun) when is_pid(Pid) andalso node(Pid) =:= node() -> - Fun(Pid); -invoke(Pid, Fun) when is_pid(Pid) -> - case invoke([Pid], Fun) of +invoke(Pid, FunOrMFA) when is_pid(Pid) andalso node(Pid) =:= node() -> + fun_or_mfa(FunOrMFA, Pid); +invoke(Pid, FunOrMFA) when is_pid(Pid) -> + case invoke([Pid], FunOrMFA) of {[{Pid, Result}], []} -> Result; {[], [{Pid, {Class, Reason, StackTrace}}]} -> erlang:raise(Class, Reason, StackTrace) end; -invoke([], _Fun) -> %% optimisation +invoke([], _FunOrMFA) -> %% optimisation {[], []}; -invoke([Pid], Fun) when node(Pid) =:= node() -> %% optimisation - case safe_invoke(Pid, Fun) of +invoke([Pid], FunOrMFA) when node(Pid) =:= node() -> %% optimisation + case safe_invoke(Pid, FunOrMFA) of {ok, _, Result} -> {[{Pid, Result}], []}; {error, _, Error} -> {[], [{Pid, Error}]} end; -invoke(Pids, Fun) when is_list(Pids) -> +invoke(Pids, FunOrMFA) when is_list(Pids) -> {LocalPids, Grouped} = group_pids_by_node(Pids), %% The use of multi_call is only safe because the timeout is %% infinity, and thus there is no process spawned in order to do @@ -91,38 +90,38 @@ invoke(Pids, Fun) when is_list(Pids) -> [] -> {[], []}; RemoteNodes -> gen_server2:multi_call( RemoteNodes, delegate(self(), RemoteNodes), - {invoke, Fun, Grouped}, infinity) + {invoke, FunOrMFA, Grouped}, infinity) end, BadPids = [{Pid, {exit, {nodedown, BadNode}, []}} || BadNode <- BadNodes, Pid <- orddict:fetch(BadNode, Grouped)], - ResultsNoNode = lists:append([safe_invoke(LocalPids, Fun) | + ResultsNoNode = lists:append([safe_invoke(LocalPids, FunOrMFA) | [Results || {_Node, Results} <- Replies]]), lists:foldl( fun ({ok, Pid, Result}, {Good, Bad}) -> {[{Pid, Result} | Good], Bad}; ({error, Pid, Error}, {Good, Bad}) -> {Good, [{Pid, Error} | Bad]} end, {[], BadPids}, ResultsNoNode). -invoke_no_result(Pid, Fun) when is_pid(Pid) andalso node(Pid) =:= node() -> - safe_invoke(Pid, Fun), %% we don't care about any error +invoke_no_result(Pid, FunOrMFA) when is_pid(Pid) andalso node(Pid) =:= node() -> + safe_invoke(Pid, FunOrMFA), %% we don't care about any error ok; -invoke_no_result(Pid, Fun) when is_pid(Pid) -> - invoke_no_result([Pid], Fun); +invoke_no_result(Pid, FunOrMFA) when is_pid(Pid) -> + invoke_no_result([Pid], FunOrMFA); -invoke_no_result([], _Fun) -> %% optimisation +invoke_no_result([], _FunOrMFA) -> %% optimisation ok; -invoke_no_result([Pid], Fun) when node(Pid) =:= node() -> %% optimisation - safe_invoke(Pid, Fun), %% must not die +invoke_no_result([Pid], FunOrMFA) when node(Pid) =:= node() -> %% optimisation + safe_invoke(Pid, FunOrMFA), %% must not die ok; -invoke_no_result(Pids, Fun) when is_list(Pids) -> +invoke_no_result(Pids, FunOrMFA) when is_list(Pids) -> {LocalPids, Grouped} = group_pids_by_node(Pids), case orddict:fetch_keys(Grouped) of [] -> ok; RemoteNodes -> gen_server2:abcast( RemoteNodes, delegate(self(), RemoteNodes), - {invoke, Fun, Grouped}) + {invoke, FunOrMFA, Grouped}) end, - safe_invoke(LocalPids, Fun), %% must not die + safe_invoke(LocalPids, FunOrMFA), %% must not die ok. monitor(Type, Pid) when node(Pid) =:= node() -> @@ -171,23 +170,29 @@ delegate(Pid, RemoteNodes) -> Name -> Name end. -safe_invoke(Pids, Fun) when is_list(Pids) -> - [safe_invoke(Pid, Fun) || Pid <- Pids]; -safe_invoke(Pid, Fun) when is_pid(Pid) -> +safe_invoke(Pids, FunOrMFA) when is_list(Pids) -> + [safe_invoke(Pid, FunOrMFA) || Pid <- Pids]; +safe_invoke(Pid, FunOrMFA) when is_pid(Pid) -> try - {ok, Pid, Fun(Pid)} + {ok, Pid, fun_or_mfa(FunOrMFA, Pid)} catch Class:Reason -> {error, Pid, {Class, Reason, erlang:get_stacktrace()}} end. +fun_or_mfa(Fun, Pid) when is_function(Fun) -> + Fun(Pid); +fun_or_mfa({M, F, A}, Pid) -> + apply(M, F, [Pid | A]). + %%---------------------------------------------------------------------------- init([Name]) -> {ok, #state{node = node(), monitors = dict:new(), name = Name}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. -handle_call({invoke, Fun, Grouped}, _From, State = #state{node = Node}) -> - {reply, safe_invoke(orddict:fetch(Node, Grouped), Fun), State, hibernate}. +handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) -> + {reply, safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), State, + hibernate}. handle_cast({monitor, Type, WantsMonitor, Pid}, State = #state{monitors = Monitors}) -> @@ -205,8 +210,8 @@ handle_cast({demonitor, Pid, Options}, State end, hibernate}; -handle_cast({invoke, Fun, Grouped}, State = #state{node = Node}) -> - safe_invoke(orddict:fetch(Node, Grouped), Fun), +handle_cast({invoke, FunOrMFA, Grouped}, State = #state{node = Node}) -> + safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), {noreply, State, hibernate}. handle_info({'DOWN', Ref, process, Pid, Info}, -- cgit v1.2.1 From 1a3856bcdd16d62a0b28c8b74f2547a4b9dc5a7a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Aug 2013 14:57:27 +0100 Subject: Use MFA for call and cast, hence avoid sending closures across the network. --- src/delegate.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 9a1f6886..03d33d01 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -139,10 +139,10 @@ demonitor({Name, Pid}, Options) -> gen_server2:cast(Name, {demonitor, Pid, Options}). call(PidOrPids, Msg) -> - invoke(PidOrPids, fun (P) -> gen_server2:call(P, Msg, infinity) end). + invoke(PidOrPids, {gen_server2, call, [Msg, infinity]}). cast(PidOrPids, Msg) -> - invoke_no_result(PidOrPids, fun (P) -> gen_server2:cast(P, Msg) end). + invoke_no_result(PidOrPids, {gen_server2, cast, [Msg]}). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From c4846ac1875ecab9757846d6463cee8f89bd6fc9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Aug 2013 14:57:49 +0100 Subject: Remove delegate beam compatibility check; we don't need that any more. --- src/rabbit_mnesia.erl | 52 ++++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6b956818..85958400 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -396,7 +396,7 @@ cluster_status(WhichNodes) -> node_info() -> {erlang:system_info(otp_release), rabbit_misc:version(), - delegate_beam_hash(), cluster_status_from_mnesia()}. + cluster_status_from_mnesia()}. node_type() -> DiscNodes = cluster_nodes(disc), @@ -570,16 +570,16 @@ check_cluster_consistency(Node) -> case rpc:call(Node, rabbit_mnesia, node_info, []) of {badrpc, _Reason} -> {error, not_found}; - {_OTP, _Rabbit, _Hash, {error, _}} -> + {_OTP, _Rabbit, {error, _}} -> {error, not_found}; - {_OTP, Rabbit, _Status} -> - %% pre-2013/04 format implies version mismatch - version_error("Rabbit", rabbit_misc:version(), Rabbit); - {OTP, Rabbit, Hash, {ok, Status}} -> - case check_consistency(OTP, Rabbit, Hash, Node, Status) of + {OTP, Rabbit, {ok, Status}} -> + case check_consistency(OTP, Rabbit, Node, Status) of {error, _} = E -> E; {ok, Res} -> {ok, Res} - end + end; + {_OTP, Rabbit, _Hash, _Status} -> + %% delegate hash checking implies version mismatch + version_error("Rabbit", rabbit_misc:version(), Rabbit) end. %%-------------------------------------------------------------------- @@ -743,17 +743,15 @@ change_extra_db_nodes(ClusterNodes0, CheckOtherNodes) -> Nodes end. -check_consistency(OTP, Rabbit, Hash) -> +check_consistency(OTP, Rabbit) -> rabbit_misc:sequence_error( [check_otp_consistency(OTP), - check_rabbit_consistency(Rabbit), - check_beam_compatibility(Hash)]). + check_rabbit_consistency(Rabbit)]). -check_consistency(OTP, Rabbit, Hash, Node, Status) -> +check_consistency(OTP, Rabbit, Node, Status) -> rabbit_misc:sequence_error( [check_otp_consistency(OTP), check_rabbit_consistency(Rabbit), - check_beam_compatibility(Hash), check_nodes_consistency(Node, Status)]). check_nodes_consistency(Node, RemoteStatus = {RemoteAllNodes, _, _}) -> @@ -789,21 +787,6 @@ check_rabbit_consistency(Remote) -> rabbit_misc:version(), Remote, "Rabbit", fun rabbit_misc:version_minor_equivalent/2). -check_beam_compatibility(RemoteHash) -> - case RemoteHash == delegate_beam_hash() of - true -> ok; - false -> {error, {incompatible_bytecode, - "Incompatible Erlang bytecode found on nodes"}} - end. - -%% The delegate module sends functions across the cluster; if it is -%% out of sync (say due to mixed compilers), we will get badfun -%% exceptions when trying to do so. Let's detect that at startup. -delegate_beam_hash() -> - {delegate, Obj, _} = code:get_object_code(delegate), - {ok, {delegate, Hash}} = beam_lib:md5(Obj), - Hash. - %% This is fairly tricky. We want to know if the node is in the state %% that a `reset' would leave it in. We cannot simply check if the %% mnesia tables aren't there because restarted RAM nodes won't have @@ -829,12 +812,13 @@ find_good_node([]) -> none; find_good_node([Node | Nodes]) -> case rpc:call(Node, rabbit_mnesia, node_info, []) of - {badrpc, _Reason} -> find_good_node(Nodes); - {_OTP, _Rabbit, _} -> find_good_node(Nodes); - {OTP, Rabbit, Hash, _} -> case check_consistency(OTP, Rabbit, Hash) of - {error, _} -> find_good_node(Nodes); - ok -> {ok, Node} - end + {badrpc, _Reason} -> find_good_node(Nodes); + %% old delegate hash check + {_OTP, _Rabbit, _Hash, _} -> find_good_node(Nodes); + {OTP, Rabbit, _} -> case check_consistency(OTP, Rabbit) of + {error, _} -> find_good_node(Nodes); + ok -> {ok, Node} + end end. is_only_clustered_disc_node() -> -- cgit v1.2.1 From 54b6cc2ad64e7954d9e584045c1a0c8724dcb4b7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 8 Aug 2013 16:23:26 +0100 Subject: tighter specs --- src/delegate.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 03d33d01..68dc1f34 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -33,14 +33,14 @@ -export_type([monitor_ref/0]). -type(monitor_ref() :: reference() | {atom(), pid()}). --type(fun_or_mfa() :: fun ((pid()) -> any()) | {atom(), atom(), [any()]}). +-type(fun_or_mfa(A) :: fun ((pid()) -> A) | {atom(), atom(), [any()]}). -spec(start_link/1 :: (non_neg_integer()) -> {'ok', pid()} | ignore | {'error', any()}). --spec(invoke/2 :: ( pid(), fun_or_mfa()) -> any(); - ([pid()], fun_or_mfa()) -> {[{pid(), any()}], - [{pid(), term()}]}). --spec(invoke_no_result/2 :: (pid() | [pid()], fun_or_mfa()) -> 'ok'). +-spec(invoke/2 :: ( pid(), fun_or_mfa(A)) -> A; + ([pid()], fun_or_mfa(A)) -> {[{pid(), A}], + [{pid(), term()}]}). +-spec(invoke_no_result/2 :: (pid() | [pid()], fun_or_mfa(any())) -> 'ok'). -spec(monitor/2 :: ('process', pid()) -> monitor_ref()). -spec(demonitor/1 :: (monitor_ref()) -> 'true'). -spec(demonitor/2 :: (monitor_ref(), ['flush']) -> 'true'). -- cgit v1.2.1 From a57c1a64506ec367226c66cb2da7ff4e853cf52b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 8 Aug 2013 17:23:13 +0100 Subject: Rename and remove duplicate credit handing on peer down --- src/rabbit_mirror_queue_slave.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 6425a855..1671dacb 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -275,7 +275,7 @@ handle_info({'DOWN', _MonitorRef, process, MPid, _Reason}, handle_info({'DOWN', _MonitorRef, process, ChPid, _Reason}, State) -> local_sender_death(ChPid, State), - noreply(sender_lifetime(ChPid, down_from_ch, State)); + noreply(maybe_forget_sender(ChPid, down_from_ch, State)); handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; @@ -660,9 +660,9 @@ forget_sender(Down1, Down2) when Down1 =/= Down2 -> true. %% Record and process lifetime events from channels. Forget all about a channel %% only when down notifications are received from both the channel and from gm. -sender_lifetime(ChPid, ChState, State = #state { sender_queues = SQ, - msg_id_status = MS, - known_senders = KS }) -> +maybe_forget_sender(ChPid, ChState, State = #state { sender_queues = SQ, + msg_id_status = MS, + known_senders = KS }) -> case dict:find(ChPid, SQ) of error -> State; @@ -811,8 +811,7 @@ process_instruction({sender_death, ChPid}, %% never got any messages. {ok, case pmon:is_monitored(ChPid, KS) of false -> State; - true -> credit_flow:peer_down(ChPid), - sender_lifetime(ChPid, down_from_gm, State) + true -> maybe_forget_sender(ChPid, down_from_gm, State) end}; process_instruction({depth, Depth}, State = #state { backing_queue = BQ, -- cgit v1.2.1 From 75006b4d2db2064f62299cdf9f9316270087af65 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Aug 2013 18:44:22 +0100 Subject: Add support for setting apply-to via rabbitmqctl. Switch to using option for priority rather than optional positional argument, with two options now that approach was starting to look confusing. --- src/rabbit_control_main.erl | 23 +++++++++++++---------- src/rabbit_policy.erl | 19 +++++++------------ 2 files changed, 20 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index f5e70365..fb8752b8 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -25,12 +25,16 @@ -define(QUIET_OPT, "-q"). -define(NODE_OPT, "-n"). -define(VHOST_OPT, "-p"). +-define(PRIORITY_OPT, "--priority"). +-define(APPLY_TO_OPT, "--apply-to"). -define(RAM_OPT, "--ram"). -define(OFFLINE_OPT, "--offline"). -define(QUIET_DEF, {?QUIET_OPT, flag}). -define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). -define(VHOST_DEF, {?VHOST_OPT, {option, "/"}}). +-define(PRIORITY_DEF, {?PRIORITY_OPT, {option, "0"}}). +-define(APPLY_TO_DEF, {?APPLY_TO_OPT, {option, "all"}}). -define(RAM_DEF, {?RAM_OPT, flag}). -define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). @@ -72,7 +76,7 @@ {clear_parameter, [?VHOST_DEF]}, {list_parameters, [?VHOST_DEF]}, - {set_policy, [?VHOST_DEF]}, + {set_policy, [?VHOST_DEF, ?PRIORITY_DEF, ?APPLY_TO_DEF]}, {clear_policy, [?VHOST_DEF]}, {list_policies, [?VHOST_DEF]}, @@ -484,16 +488,15 @@ action(list_parameters, Node, [], Opts, Inform) -> rpc_call(Node, rabbit_runtime_parameters, list_formatted, [VHostArg]), rabbit_runtime_parameters:info_keys()); -action(set_policy, Node, [Key, Pattern, Defn | Prio], Opts, Inform) - when Prio == [] orelse length(Prio) == 1 -> - Msg = "Setting policy ~p for pattern ~p to ~p", - {InformMsg, Prio1} = case Prio of [] -> {Msg, undefined}; - [P] -> {Msg ++ " with priority ~s", P} - end, +action(set_policy, Node, [Key, Pattern, Defn], Opts, Inform) -> + Msg = "Setting policy ~p for pattern ~p to ~p with priority ~p", VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - Inform(InformMsg, [Key, Pattern, Defn] ++ Prio), - rpc_call(Node, rabbit_policy, parse_set, - [VHostArg, list_to_binary(Key), Pattern, Defn, Prio1]); + PriorityArg = proplists:get_value(?PRIORITY_OPT, Opts), + ApplyToArg = list_to_binary(proplists:get_value(?APPLY_TO_OPT, Opts)), + Inform(Msg, [Key, Pattern, Defn, PriorityArg]), + rpc_call( + Node, rabbit_policy, parse_set, + [VHostArg, list_to_binary(Key), Pattern, Defn, PriorityArg, ApplyToArg]); action(clear_policy, Node, [Key], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 5b2e2da2..f4a5de5a 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -28,7 +28,7 @@ -export([invalidate/0, recover/0]). -export([name/1, get/2, set/1]). -export([validate/4, notify/4, notify_clear/3]). --export([parse_set/5, set/6, delete/2, lookup/2, list/0, list/1, +-export([parse_set/6, set/6, delete/2, lookup/2, list/0, list/1, list_formatted/1, info_keys/0]). -rabbit_boot_step({?MODULE, @@ -108,22 +108,21 @@ invalid_file() -> %%---------------------------------------------------------------------------- -parse_set(VHost, Name, Pattern, Definition, undefined) -> - parse_set0(VHost, Name, Pattern, Definition, 0); -parse_set(VHost, Name, Pattern, Definition, Priority) -> +parse_set(VHost, Name, Pattern, Definition, Priority, ApplyTo) -> try list_to_integer(Priority) of - Num -> parse_set0(VHost, Name, Pattern, Definition, Num) + Num -> parse_set0(VHost, Name, Pattern, Definition, Num, ApplyTo) catch error:badarg -> {error, "~p priority must be a number", [Priority]} end. -parse_set0(VHost, Name, Pattern, Defn, Priority) -> +parse_set0(VHost, Name, Pattern, Defn, Priority, ApplyTo) -> case rabbit_misc:json_decode(Defn) of {ok, JSON} -> set0(VHost, Name, [{<<"pattern">>, list_to_binary(Pattern)}, {<<"definition">>, rabbit_misc:json_to_term(JSON)}, - {<<"priority">>, Priority}]); + {<<"priority">>, Priority}, + {<<"apply-to">>, ApplyTo}]); error -> {error_string, "JSON decoding error"} end. @@ -141,11 +140,7 @@ set(VHost, Name, Pattern, Definition, Priority, ApplyTo) -> end}], set0(VHost, Name, PolicyProps). -set0(VHost, Name, Term0) -> - Term = case pget(<<"apply-to">>, Term0) of - undefined -> [{<<"apply-to">>, <<"all">>} | Term0]; - _ -> Term0 - end, +set0(VHost, Name, Term) -> rabbit_runtime_parameters:set_any(VHost, <<"policy">>, Name, Term). delete(VHost, Name) -> -- cgit v1.2.1 From f721a7fa14b23c391fed908695491e526c0f2391 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 9 Aug 2013 07:38:10 +0100 Subject: refactor: better name for function application plus lose the type check; we are not that paranoid. --- src/delegate.erl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 68dc1f34..5277e59f 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -64,7 +64,7 @@ start_link(Num) -> gen_server2:start_link({local, Name}, ?MODULE, [Name], []). invoke(Pid, FunOrMFA) when is_pid(Pid) andalso node(Pid) =:= node() -> - fun_or_mfa(FunOrMFA, Pid); + apply1(FunOrMFA, Pid); invoke(Pid, FunOrMFA) when is_pid(Pid) -> case invoke([Pid], FunOrMFA) of {[{Pid, Result}], []} -> @@ -174,15 +174,13 @@ safe_invoke(Pids, FunOrMFA) when is_list(Pids) -> [safe_invoke(Pid, FunOrMFA) || Pid <- Pids]; safe_invoke(Pid, FunOrMFA) when is_pid(Pid) -> try - {ok, Pid, fun_or_mfa(FunOrMFA, Pid)} + {ok, Pid, apply1(FunOrMFA, Pid)} catch Class:Reason -> {error, Pid, {Class, Reason, erlang:get_stacktrace()}} end. -fun_or_mfa(Fun, Pid) when is_function(Fun) -> - Fun(Pid); -fun_or_mfa({M, F, A}, Pid) -> - apply(M, F, [Pid | A]). +apply1({M, F, A}, Arg) -> apply(M, F, [Arg | A]); +apply1(Fun, Arg) -> Fun(Arg). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 1dd07cdd30ad0c2e955b297066ce2e83129d08ed Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Aug 2013 11:54:36 +0100 Subject: So it turns out that the policy tests fail, since we now depend on rabbit_misc:parse_arguments() to fill in defaults for us. But that's not at all unreasonable I think, and in fact rabbit_tests:control_action() does some rather odd things around options. Rather than rewrite everything, just introduce a new variant that is rather closer to what rabbitmqctl actually does, and use that for policy tests. Oh, and add some more tests, that's always good. --- src/rabbit_control_main.erl | 27 +++++++++++------- src/rabbit_tests.erl | 69 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index fb8752b8..38a07b6a 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -17,7 +17,8 @@ -module(rabbit_control_main). -include("rabbit.hrl"). --export([start/0, stop/0, action/5, sync_queue/1, cancel_sync_queue/1]). +-export([start/0, stop/0, parse_arguments/2, action/5, + sync_queue/1, cancel_sync_queue/1]). -define(RPC_TIMEOUT, infinity). -define(EXTERNAL_CHECK_INTERVAL, 1000). @@ -131,19 +132,13 @@ start() -> {ok, [[NodeStr|_]|_]} = init:get_argument(nodename), {Command, Opts, Args} = - case rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS(NodeStr), - init:get_plain_arguments()) - of + case parse_arguments(init:get_plain_arguments(), NodeStr) of {ok, Res} -> Res; no_command -> print_error("could not recognise command", []), usage() end, - Opts1 = [case K of - ?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)}; - _ -> {K, V} - end || {K, V} <- Opts], - Quiet = proplists:get_bool(?QUIET_OPT, Opts1), - Node = proplists:get_value(?NODE_OPT, Opts1), + Quiet = proplists:get_bool(?QUIET_OPT, Opts), + Node = proplists:get_value(?NODE_OPT, Opts), Inform = case Quiet of true -> fun (_Format, _Args1) -> ok end; false -> fun (Format, Args1) -> @@ -234,6 +229,18 @@ usage() -> io:format("~s", [rabbit_ctl_usage:usage()]), rabbit_misc:quit(1). +parse_arguments(Args, NodeStr) -> + case rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS(NodeStr), Args) of + {ok, {Cmd, Opts0, Args}} -> + Opts = [case K of + ?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)}; + _ -> {K, V} + end || {K, V} <- Opts0], + {ok, {Cmd, Opts, Args}}; + E -> + E + end. + %%---------------------------------------------------------------------------- action(stop, Node, Args, _Opts, Inform) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 21c54f3e..ccea636d 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -60,6 +60,7 @@ all_tests() -> passed = test_user_management(), passed = test_runtime_parameters(), passed = test_policy_validation(), + passed = test_policy_opts_validation(), passed = test_ha_policy_validation(), passed = test_server_status(), passed = test_amqp_connection_refusal(), @@ -1083,29 +1084,57 @@ test_runtime_parameters() -> test_policy_validation() -> rabbit_runtime_parameters_test:register_policy_validator(), - SetPol = - fun (Key, Val) -> - control_action( - set_policy, - ["name", ".*", rabbit_misc:format("{\"~s\":~p}", [Key, Val])]) - end, + SetPol = fun (Key, Val) -> + control_action_opts( + ["set_policy", "name", ".*", + rabbit_misc:format("{\"~s\":~p}", [Key, Val])]) + end, - ok = SetPol("testeven", []), - ok = SetPol("testeven", [1, 2]), - ok = SetPol("testeven", [1, 2, 3, 4]), - ok = SetPol("testpos", [2, 5, 5678]), + ok = SetPol("testeven", []), + ok = SetPol("testeven", [1, 2]), + ok = SetPol("testeven", [1, 2, 3, 4]), + ok = SetPol("testpos", [2, 5, 5678]), - {error_string, _} = SetPol("testpos", [-1, 0, 1]), - {error_string, _} = SetPol("testeven", [ 1, 2, 3]), + error = SetPol("testpos", [-1, 0, 1]), + error = SetPol("testeven", [ 1, 2, 3]), ok = control_action(clear_policy, ["name"]), rabbit_runtime_parameters_test:unregister_policy_validator(), passed. +test_policy_opts_validation() -> + Set = fun (Extra) -> control_action_opts( + ["set_policy", "name", ".*", "{\"ha-mode\":\"all\"}" + | Extra]) end, + OK = fun (Extra) -> ok = Set(Extra) end, + Fail = fun (Extra) -> error = Set(Extra) end, + + OK ([]), + + OK (["--priority", "0"]), + OK (["--priority", "3"]), + Fail(["--priority", "banana"]), + Fail(["--priority"]), + + OK (["--apply-to", "all"]), + OK (["--apply-to", "queues"]), + Fail(["--apply-to", "bananas"]), + Fail(["--apply-to"]), + + OK (["--priority", "3", "--apply-to", "queues"]), + Fail(["--priority", "banana", "--apply-to", "queues"]), + Fail(["--priority", "3", "--apply-to", "bananas"]), + + Fail(["--offline"]), + + ok = control_action(clear_policy, ["name"]), + passed. + test_ha_policy_validation() -> - Set = fun (JSON) -> control_action(set_policy, ["name", ".*", JSON]) end, + Set = fun (JSON) -> control_action_opts( + ["set_policy", "name", ".*", JSON]) end, OK = fun (JSON) -> ok = Set(JSON) end, - Fail = fun (JSON) -> {error_string, _} = Set(JSON) end, + Fail = fun (JSON) -> error = Set(JSON) end, OK ("{\"ha-mode\":\"all\"}"), Fail("{\"ha-mode\":\"made_up\"}"), @@ -1611,6 +1640,18 @@ control_action(Command, Node, Args, Opts) -> Other end. +control_action_opts(Raw) -> + NodeStr = atom_to_list(node()), + case rabbit_control_main:parse_arguments(Raw, NodeStr) of + {ok, {Cmd, Opts, Args}} -> + case control_action(Cmd, node(), Args, Opts) of + ok -> ok; + _ -> error + end; + _ -> + error + end. + info_action(Command, Args, CheckVHost) -> ok = control_action(Command, []), if CheckVHost -> ok = control_action(Command, [], ["-p", "/"]); -- cgit v1.2.1 From fb43be6543d3b4c60a5a6b338d1d70661fa4afaf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 13 Aug 2013 10:39:39 +0100 Subject: Fix inadvertent match of Args. That's what you get for only running the test suite. --- src/rabbit_control_main.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 38a07b6a..07fdabb9 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -229,8 +229,9 @@ usage() -> io:format("~s", [rabbit_ctl_usage:usage()]), rabbit_misc:quit(1). -parse_arguments(Args, NodeStr) -> - case rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS(NodeStr), Args) of +parse_arguments(CmdLine, NodeStr) -> + case rabbit_misc:parse_arguments( + ?COMMANDS, ?GLOBAL_DEFS(NodeStr), CmdLine) of {ok, {Cmd, Opts0, Args}} -> Opts = [case K of ?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)}; -- cgit v1.2.1 From 8909bee026f3ee6b7f20828ce59d5a30de1919fd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 15 Aug 2013 12:47:18 +0100 Subject: Revert b2db7048b839 (which in turn mostly reverted dd08c9204760), i.e. return to the broken bug25644 implementation. --- src/delegate.erl | 87 +++++++++++++++++++++++++++++++-------- src/pmon.erl | 42 +++++++++++-------- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 10 ++--- 4 files changed, 101 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index f680d94a..5277e59f 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -18,22 +18,33 @@ -behaviour(gen_server2). --export([start_link/1, invoke_no_result/2, invoke/2, call/2, cast/2]). +-export([start_link/1, invoke_no_result/2, invoke/2, monitor/2, + demonitor/1, demonitor/2, call/2, cast/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-record(state, {node, monitors, name}). + %%---------------------------------------------------------------------------- -ifdef(use_specs). +-export_type([monitor_ref/0]). + +-type(monitor_ref() :: reference() | {atom(), pid()}). -type(fun_or_mfa(A) :: fun ((pid()) -> A) | {atom(), atom(), [any()]}). + -spec(start_link/1 :: (non_neg_integer()) -> {'ok', pid()} | ignore | {'error', any()}). -spec(invoke/2 :: ( pid(), fun_or_mfa(A)) -> A; ([pid()], fun_or_mfa(A)) -> {[{pid(), A}], [{pid(), term()}]}). -spec(invoke_no_result/2 :: (pid() | [pid()], fun_or_mfa(any())) -> 'ok'). +-spec(monitor/2 :: ('process', pid()) -> monitor_ref()). +-spec(demonitor/1 :: (monitor_ref()) -> 'true'). +-spec(demonitor/2 :: (monitor_ref(), ['flush']) -> 'true'). + -spec(call/2 :: ( pid(), any()) -> any(); ([pid()], any()) -> {[{pid(), any()}], [{pid(), term()}]}). @@ -49,7 +60,8 @@ %%---------------------------------------------------------------------------- start_link(Num) -> - gen_server2:start_link({local, delegate_name(Num)}, ?MODULE, [], []). + Name = delegate_name(Num), + gen_server2:start_link({local, Name}, ?MODULE, [Name], []). invoke(Pid, FunOrMFA) when is_pid(Pid) andalso node(Pid) =:= node() -> apply1(FunOrMFA, Pid); @@ -77,7 +89,7 @@ invoke(Pids, FunOrMFA) when is_list(Pids) -> case orddict:fetch_keys(Grouped) of [] -> {[], []}; RemoteNodes -> gen_server2:multi_call( - RemoteNodes, delegate(RemoteNodes), + RemoteNodes, delegate(self(), RemoteNodes), {invoke, FunOrMFA, Grouped}, infinity) end, BadPids = [{Pid, {exit, {nodedown, BadNode}, []}} || @@ -105,12 +117,27 @@ invoke_no_result(Pids, FunOrMFA) when is_list(Pids) -> {LocalPids, Grouped} = group_pids_by_node(Pids), case orddict:fetch_keys(Grouped) of [] -> ok; - RemoteNodes -> gen_server2:abcast(RemoteNodes, delegate(RemoteNodes), - {invoke, FunOrMFA, Grouped}) + RemoteNodes -> gen_server2:abcast( + RemoteNodes, delegate(self(), RemoteNodes), + {invoke, FunOrMFA, Grouped}) end, safe_invoke(LocalPids, FunOrMFA), %% must not die ok. +monitor(Type, Pid) when node(Pid) =:= node() -> + erlang:monitor(Type, Pid); +monitor(Type, Pid) -> + Name = delegate(Pid, [node(Pid)]), + gen_server2:cast(Name, {monitor, Type, self(), Pid}), + {Name, Pid}. + +demonitor(Ref) -> ?MODULE:demonitor(Ref, []). + +demonitor(Ref, Options) when is_reference(Ref) -> + erlang:demonitor(Ref, Options); +demonitor({Name, Pid}, Options) -> + gen_server2:cast(Name, {demonitor, Pid, Options}). + call(PidOrPids, Msg) -> invoke(PidOrPids, {gen_server2, call, [Msg, infinity]}). @@ -133,10 +160,10 @@ group_pids_by_node(Pids) -> delegate_name(Hash) -> list_to_atom("delegate_" ++ integer_to_list(Hash)). -delegate(RemoteNodes) -> +delegate(Pid, RemoteNodes) -> case get(delegate) of undefined -> Name = delegate_name( - erlang:phash2(self(), + erlang:phash2(Pid, delegate_sup:count(RemoteNodes))), put(delegate, Name), Name; @@ -157,23 +184,49 @@ apply1(Fun, Arg) -> Fun(Arg). %%---------------------------------------------------------------------------- -init([]) -> - {ok, node(), hibernate, +init([Name]) -> + {ok, #state{node = node(), monitors = dict:new(), name = Name}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. -handle_call({invoke, FunOrMFA, Grouped}, _From, Node) -> - {reply, safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), Node, +handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) -> + {reply, safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), State, hibernate}. -handle_cast({invoke, FunOrMFA, Grouped}, Node) -> +handle_cast({monitor, Type, WantsMonitor, Pid}, + State = #state{monitors = Monitors}) -> + Ref = erlang:monitor(Type, Pid), + Monitors1 = dict:store(Pid, {WantsMonitor, Ref}, Monitors), + {noreply, State#state{monitors = Monitors1}, hibernate}; + +handle_cast({demonitor, Pid, Options}, + State = #state{monitors = Monitors}) -> + {noreply, case dict:find(Pid, Monitors) of + {ok, {_WantsMonitor, Ref}} -> + erlang:demonitor(Ref, Options), + State#state{monitors = dict:erase(Pid, Monitors)}; + error -> + State + end, hibernate}; + +handle_cast({invoke, FunOrMFA, Grouped}, State = #state{node = Node}) -> safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), - {noreply, Node, hibernate}. + {noreply, State, hibernate}. + +handle_info({'DOWN', Ref, process, Pid, Info}, + State = #state{monitors = Monitors, name = Name}) -> + {noreply, case dict:find(Pid, Monitors) of + {ok, {WantsMonitor, Ref}} -> + WantsMonitor ! {'DOWN', {Name, Pid}, process, Pid, Info}, + State#state{monitors = dict:erase(Pid, Monitors)}; + error -> + State + end, hibernate}; -handle_info(_Info, Node) -> - {noreply, Node, hibernate}. +handle_info(_Info, State) -> + {noreply, State, hibernate}. terminate(_Reason, _State) -> ok. -code_change(_OldVsn, Node, _Extra) -> - {ok, Node}. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/src/pmon.erl b/src/pmon.erl index b9db66fb..86308167 100644 --- a/src/pmon.erl +++ b/src/pmon.erl @@ -16,22 +16,26 @@ -module(pmon). --export([new/0, monitor/2, monitor_all/2, demonitor/2, is_monitored/2, erase/2, - monitored/1, is_empty/1]). +-export([new/0, new/1, monitor/2, monitor_all/2, demonitor/2, + is_monitored/2, erase/2, monitored/1, is_empty/1]). -compile({no_auto_import, [monitor/2]}). +-record(state, {dict, module}). + -ifdef(use_specs). %%---------------------------------------------------------------------------- -export_type([?MODULE/0]). --opaque(?MODULE() :: dict()). +-opaque(?MODULE() :: #state{dict :: dict(), + module :: atom()}). -type(item() :: pid() | {atom(), node()}). -spec(new/0 :: () -> ?MODULE()). +-spec(new/1 :: ('erlang' | 'delegate') -> ?MODULE()). -spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()). -spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). -spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). @@ -42,29 +46,33 @@ -endif. -new() -> dict:new(). +new() -> new(erlang). + +new(Module) -> #state{dict = dict:new(), + module = Module}. -monitor(Item, M) -> +monitor(Item, S = #state{dict = M, module = Module}) -> case dict:is_key(Item, M) of - true -> M; - false -> dict:store(Item, erlang:monitor(process, Item), M) + true -> S; + false -> S#state{dict = dict:store( + Item, Module:monitor(process, Item), M)} end. -monitor_all([], M) -> M; %% optimisation -monitor_all([Item], M) -> monitor(Item, M); %% optimisation -monitor_all(Items, M) -> lists:foldl(fun monitor/2, M, Items). +monitor_all([], S) -> S; %% optimisation +monitor_all([Item], S) -> monitor(Item, S); %% optimisation +monitor_all(Items, S) -> lists:foldl(fun monitor/2, S, Items). -demonitor(Item, M) -> +demonitor(Item, S = #state{dict = M, module = Module}) -> case dict:find(Item, M) of - {ok, MRef} -> erlang:demonitor(MRef), - dict:erase(Item, M); + {ok, MRef} -> Module:demonitor(MRef), + S#state{dict = dict:erase(Item, M)}; error -> M end. -is_monitored(Item, M) -> dict:is_key(Item, M). +is_monitored(Item, #state{dict = M}) -> dict:is_key(Item, M). -erase(Item, M) -> dict:erase(Item, M). +erase(Item, S = #state{dict = M}) -> S#state{dict = dict:erase(Item, M)}. -monitored(M) -> dict:fetch_keys(M). +monitored(#state{dict = M}) -> dict:fetch_keys(M). -is_empty(M) -> dict:size(M) == 0. +is_empty(#state{dict = M}) -> dict:size(M) == 0. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e61cba02..6e0eb9bf 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -146,7 +146,7 @@ init_state(Q) -> exclusive_consumer = none, has_had_consumers = false, active_consumers = queue:new(), - senders = pmon:new(), + senders = pmon:new(delegate), msg_id_to_channel = gb_trees:empty(), status = running}, rabbit_event:init_stats_timer(State, #q.stats_timer). diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index b74ae3a5..82819cbe 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -118,7 +118,7 @@ init(Q = #amqqueue { name = QName }) -> msg_id_ack = dict:new(), msg_id_status = dict:new(), - known_senders = pmon:new(), + known_senders = pmon:new(delegate), depth_delta = undefined }, @@ -270,7 +270,8 @@ handle_info({'DOWN', _MonitorRef, process, MPid, _Reason}, noreply(State); handle_info({'DOWN', _MonitorRef, process, ChPid, _Reason}, State) -> - noreply(local_sender_death(ChPid, State)); + local_sender_death(ChPid, State), + noreply(State); handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; @@ -601,7 +602,7 @@ stop_rate_timer(State) -> rabbit_misc:stop_timer(State, #state.rate_timer_ref). ensure_monitoring(ChPid, State = #state { known_senders = KS }) -> State #state { known_senders = pmon:monitor(ChPid, KS) }. -local_sender_death(ChPid, State = #state { known_senders = KS }) -> +local_sender_death(ChPid, #state { known_senders = KS }) -> %% The channel will be monitored iff we have received a delivery %% from it but not heard about its death from the master. So if it %% is monitored we need to point the death out to the master (see @@ -609,8 +610,7 @@ local_sender_death(ChPid, State = #state { known_senders = KS }) -> ok = case pmon:is_monitored(ChPid, KS) of false -> ok; true -> confirm_sender_death(ChPid) - end, - State. + end. confirm_sender_death(Pid) -> %% We have to deal with the possibility that we'll be promoted to -- cgit v1.2.1 From 5e69009198ab230c314aa6c2678accd55cf6ba6e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 15 Aug 2013 12:49:32 +0100 Subject: Store monitors correctly, with a two level dictionary, mapping MonitoredPid -> MonitoringPid -> Ref and MonitoredPid -> Ref -> MonitoringPid. --- src/delegate.erl | 67 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 5277e59f..44a909dc 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -136,7 +136,7 @@ demonitor(Ref) -> ?MODULE:demonitor(Ref, []). demonitor(Ref, Options) when is_reference(Ref) -> erlang:demonitor(Ref, Options); demonitor({Name, Pid}, Options) -> - gen_server2:cast(Name, {demonitor, Pid, Options}). + gen_server2:cast(Name, {demonitor, self(), Pid, Options}). call(PidOrPids, Msg) -> invoke(PidOrPids, {gen_server2, call, [Msg, infinity]}). @@ -185,7 +185,7 @@ apply1(Fun, Arg) -> Fun(Arg). %%---------------------------------------------------------------------------- init([Name]) -> - {ok, #state{node = node(), monitors = dict:new(), name = Name}, hibernate, + {ok, #state{node = node(), monitors = ddict_new(), name = Name}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) -> @@ -195,15 +195,16 @@ handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) -> handle_cast({monitor, Type, WantsMonitor, Pid}, State = #state{monitors = Monitors}) -> Ref = erlang:monitor(Type, Pid), - Monitors1 = dict:store(Pid, {WantsMonitor, Ref}, Monitors), + Monitors1 = ddict_store(Pid, WantsMonitor, Ref, Monitors), {noreply, State#state{monitors = Monitors1}, hibernate}; -handle_cast({demonitor, Pid, Options}, +handle_cast({demonitor, WantsMonitor, Pid, Options}, State = #state{monitors = Monitors}) -> - {noreply, case dict:find(Pid, Monitors) of - {ok, {_WantsMonitor, Ref}} -> + {noreply, case ddict_find_f(Pid, WantsMonitor, Monitors) of + {ok, Ref} -> erlang:demonitor(Ref, Options), - State#state{monitors = dict:erase(Pid, Monitors)}; + State#state{monitors = ddict_erase_f( + Pid, WantsMonitor, Monitors)}; error -> State end, hibernate}; @@ -214,10 +215,10 @@ handle_cast({invoke, FunOrMFA, Grouped}, State = #state{node = Node}) -> handle_info({'DOWN', Ref, process, Pid, Info}, State = #state{monitors = Monitors, name = Name}) -> - {noreply, case dict:find(Pid, Monitors) of - {ok, {WantsMonitor, Ref}} -> + {noreply, case ddict_find_r(Pid, Ref, Monitors) of + {ok, WantsMonitor} -> WantsMonitor ! {'DOWN', {Name, Pid}, process, Pid, Info}, - State#state{monitors = dict:erase(Pid, Monitors)}; + State#state{monitors = ddict_erase_r(Pid, Ref, Monitors)}; error -> State end, hibernate}; @@ -230,3 +231,49 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. + +%%---------------------------------------------------------------------------- + +ddict_new() -> dict:new(). + +ddict_store(Key, Val1, Val2, Dict) -> + V = case dict:find(Key, Dict) of + {ok, {DictF, DictR}} -> {dict:store(Val1, Val2, DictF), + dict:store(Val2, Val1, DictR)}; + error -> {dict:new(), dict:new()} + end, + dict:store(Key, V, Dict). + +ddict_find_f(Key, Val1, Dict) -> ddict_find(Key, Val1, Dict, fun select_f/1). +ddict_find_r(Key, Val2, Dict) -> ddict_find(Key, Val2, Dict, fun select_r/1). + +ddict_find(Key, ValX, Dict, Select) -> + case dict:find(Key, Dict) of + {ok, Dicts} -> {DictX, _} = Select(Dicts), + dict:find(ValX, DictX); + error -> error + end. + +ddict_erase_f(Key, Val1, Dict) -> ddict_erase(Key, Val1, Dict, fun select_f/1). +ddict_erase_r(Key, Val2, Dict) -> ddict_erase(Key, Val2, Dict, fun select_r/1). + +ddict_erase(Key, ValX, Dict, Select) -> + case dict:find(Key, Dict) of + {ok, Dicts} -> + {DictX, DictY} = Select(Dicts), + Dicts1 = {D, _} = + case dict:find(ValX, DictX) of + {ok, ValY} -> Select({dict:erase(ValX, DictX), + dict:erase(ValY, DictY)}); + error -> Dicts + end, + case dict:size(D) of + 0 -> dict:erase(Key, Dict); + _ -> dict:store(Key, Dicts1, Dict) + end; + error -> + Dict + end. + +select_f({A, B}) -> {A, B}. +select_r({A, B}) -> {B, A}. -- cgit v1.2.1 From 39fa88f5eab534561327f1ec5ba7413b8babf7b0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 15 Aug 2013 12:51:21 +0100 Subject: Explain what this is. --- src/delegate.erl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 44a909dc..15a15cf9 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -234,6 +234,9 @@ code_change(_OldVsn, State, _Extra) -> %%---------------------------------------------------------------------------- +%% A two level nested dictionary, with the second level being +%% bidirectional. i.e. we map A->B->C and A->C->B. + ddict_new() -> dict:new(). ddict_store(Key, Val1, Val2, Dict) -> -- cgit v1.2.1 From eb18c78fcfcff9622a43e8823dee83f8348ca3c0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 15 Aug 2013 17:33:18 +0100 Subject: Make delete and unbind idempotent. --- src/rabbit_binding.erl | 41 +++++++++++++++++++++++++---------------- src/rabbit_channel.erl | 14 ++++++++++---- 2 files changed, 35 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 91f42e9c..98b427c7 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -152,7 +152,7 @@ exists(Binding) -> binding_action( Binding, fun (_Src, _Dst, B) -> rabbit_misc:const(mnesia:read({rabbit_route, B}) /= []) - end). + end, fun not_found_or_absent_errs/1). add(Binding) -> add(Binding, fun (_Src, _Dst) -> ok end). @@ -177,7 +177,7 @@ add(Binding, InnerFun) -> {error, _} = Err -> rabbit_misc:const(Err) end - end). + end, fun not_found_or_absent_errs/1). add(Src, Dst, B) -> [SrcDurable, DstDurable] = [durable(E) || E <- [Src, Dst]], @@ -200,14 +200,15 @@ remove(Binding, InnerFun) -> binding_action( Binding, fun (Src, Dst, B) -> - case mnesia:read(rabbit_route, B, write) of - [] -> rabbit_misc:const({error, binding_not_found}); - [_] -> case InnerFun(Src, Dst) of - ok -> remove(Src, Dst, B); - {error, _} = Err -> rabbit_misc:const(Err) - end + case mnesia:read(rabbit_route, B, write) =:= [] andalso + mnesia:read(rabbit_durable_route, B, write) =/= [] of + true -> rabbit_misc:const({error, binding_not_found}); + false -> case InnerFun(Src, Dst) of + ok -> remove(Src, Dst, B); + {error, _} = Err -> rabbit_misc:const(Err) + end end - end). + end, fun absent_errs_only/1). remove(Src, Dst, B) -> ok = sync_route(#route{binding = B}, durable(Src), durable(Dst), @@ -306,13 +307,13 @@ durable(#amqqueue{durable = D}) -> D. binding_action(Binding = #binding{source = SrcName, destination = DstName, - args = Arguments}, Fun) -> + args = Arguments}, Fun, ErrFun) -> call_with_source_and_destination( SrcName, DstName, fun (Src, Dst) -> SortedArgs = rabbit_misc:sort_field_table(Arguments), Fun(Src, Dst, Binding#binding{args = SortedArgs}) - end). + end, ErrFun). delete_object(Tab, Record, LockKind) -> %% this 'guarded' delete prevents unnecessary writes to the mnesia @@ -339,13 +340,9 @@ sync_transient_route(Route, Fun) -> ok = Fun(rabbit_route, Route, write), ok = Fun(rabbit_reverse_route, reverse_route(Route), write). -call_with_source_and_destination(SrcName, DstName, Fun) -> +call_with_source_and_destination(SrcName, DstName, Fun, ErrFun) -> SrcTable = table_for_resource(SrcName), DstTable = table_for_resource(DstName), - ErrFun = fun (Names) -> - Errs = [not_found_or_absent(Name) || Name <- Names], - rabbit_misc:const({error, {resources_missing, Errs}}) - end, rabbit_misc:execute_mnesia_tx_with_tail( fun () -> case {mnesia:read({SrcTable, SrcName}), @@ -357,6 +354,18 @@ call_with_source_and_destination(SrcName, DstName, Fun) -> end end). +not_found_or_absent_errs(Names) -> + Errs = [not_found_or_absent(Name) || Name <- Names], + rabbit_misc:const({error, {resources_missing, Errs}}). + +absent_errs_only(Names) -> + Errs = [E || Name <- Names, + {absent, _Q} = E <- [not_found_or_absent(Name)]], + rabbit_misc:const(case Errs of + [] -> ok; + _ -> {error, {resources_missing, Errs}} + end). + table_for_resource(#resource{kind = exchange}) -> rabbit_exchange; table_for_resource(#resource{kind = queue}) -> rabbit_queue. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d6c1e8c0..2cfbd96d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -934,7 +934,7 @@ handle_method(#'exchange.delete'{exchange = ExchangeNameBin, check_configure_permitted(ExchangeName, State), case rabbit_exchange:delete(ExchangeName, IfUnused) of {error, not_found} -> - rabbit_misc:not_found(ExchangeName); + return_ok(State, NoWait, #'exchange.delete_ok'{}); {error, in_use} -> precondition_failed("~s in use", [rabbit_misc:rs(ExchangeName)]); ok -> @@ -1047,9 +1047,15 @@ handle_method(#'queue.delete'{queue = QueueNameBin, _, State = #ch{conn_pid = ConnPid}) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), check_configure_permitted(QueueName, State), - case rabbit_amqqueue:with_exclusive_access_or_die( - QueueName, ConnPid, - fun (Q) -> rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) end) of + case rabbit_amqqueue:with( + QueueName, + fun (Q) -> + rabbit_amqqueue:check_exclusive_access(Q, ConnPid), + rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) + end, + fun (not_found) -> {ok, 0}; + ({absent, Q}) -> rabbit_misc:absent(Q) + end) of {error, in_use} -> precondition_failed("~s in use", [rabbit_misc:rs(QueueName)]); {error, not_empty} -> -- cgit v1.2.1 From 3e656c35c0bb749a0b16c1435335e0c8daed3ad9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 16 Aug 2013 14:37:04 +0100 Subject: Simpler to maintain MonitoredPid -> {Ref, MonitoringPidSet}. --- src/delegate.erl | 98 ++++++++++++++++++-------------------------------------- 1 file changed, 32 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 15a15cf9..6e216cce 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -185,7 +185,7 @@ apply1(Fun, Arg) -> Fun(Arg). %%---------------------------------------------------------------------------- init([Name]) -> - {ok, #state{node = node(), monitors = ddict_new(), name = Name}, hibernate, + {ok, #state{node = node(), monitors = dict:new(), name = Name}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) -> @@ -194,20 +194,31 @@ handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) -> handle_cast({monitor, Type, WantsMonitor, Pid}, State = #state{monitors = Monitors}) -> - Ref = erlang:monitor(Type, Pid), - Monitors1 = ddict_store(Pid, WantsMonitor, Ref, Monitors), + Monitors1 = case dict:find(Pid, Monitors) of + {ok, {Ref, Set}} -> + Set1 = sets:add_element(WantsMonitor, Set), + dict:store(Pid, {Ref, Set1}, Monitors); + error -> + Ref = erlang:monitor(Type, Pid), + Set = sets:from_list([WantsMonitor]), + dict:store(Pid, {Ref, Set}, Monitors) + end, {noreply, State#state{monitors = Monitors1}, hibernate}; handle_cast({demonitor, WantsMonitor, Pid, Options}, State = #state{monitors = Monitors}) -> - {noreply, case ddict_find_f(Pid, WantsMonitor, Monitors) of - {ok, Ref} -> - erlang:demonitor(Ref, Options), - State#state{monitors = ddict_erase_f( - Pid, WantsMonitor, Monitors)}; + Monitors1 = case dict:find(Pid, Monitors) of + {ok, {Ref, Set}} -> + Set1 = sets:del_element(WantsMonitor, Set), + case sets:size(Set1) of + 0 -> erlang:demonitor(Ref, Options), + dict:erase(Pid, Monitors); + _ -> dict:store(Pid, {Ref, Set1}, Monitors) + end; error -> - State - end, hibernate}; + Monitors + end, + {noreply, State#state{monitors = Monitors1}, hibernate}; handle_cast({invoke, FunOrMFA, Grouped}, State = #state{node = Node}) -> safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), @@ -215,13 +226,17 @@ handle_cast({invoke, FunOrMFA, Grouped}, State = #state{node = Node}) -> handle_info({'DOWN', Ref, process, Pid, Info}, State = #state{monitors = Monitors, name = Name}) -> - {noreply, case ddict_find_r(Pid, Ref, Monitors) of - {ok, WantsMonitor} -> - WantsMonitor ! {'DOWN', {Name, Pid}, process, Pid, Info}, - State#state{monitors = ddict_erase_r(Pid, Ref, Monitors)}; - error -> - State - end, hibernate}; + {noreply, + case dict:find(Pid, Monitors) of + {ok, {Ref, Set}} -> + sets:fold( + fun (WantsMonitor, none) -> + WantsMonitor ! {'DOWN', {Name, Pid}, process, Pid, Info} + end, none, Set), + State#state{monitors = dict:erase(Pid, Monitors)}; + error -> + State + end, hibernate}; handle_info(_Info, State) -> {noreply, State, hibernate}. @@ -231,52 +246,3 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. - -%%---------------------------------------------------------------------------- - -%% A two level nested dictionary, with the second level being -%% bidirectional. i.e. we map A->B->C and A->C->B. - -ddict_new() -> dict:new(). - -ddict_store(Key, Val1, Val2, Dict) -> - V = case dict:find(Key, Dict) of - {ok, {DictF, DictR}} -> {dict:store(Val1, Val2, DictF), - dict:store(Val2, Val1, DictR)}; - error -> {dict:new(), dict:new()} - end, - dict:store(Key, V, Dict). - -ddict_find_f(Key, Val1, Dict) -> ddict_find(Key, Val1, Dict, fun select_f/1). -ddict_find_r(Key, Val2, Dict) -> ddict_find(Key, Val2, Dict, fun select_r/1). - -ddict_find(Key, ValX, Dict, Select) -> - case dict:find(Key, Dict) of - {ok, Dicts} -> {DictX, _} = Select(Dicts), - dict:find(ValX, DictX); - error -> error - end. - -ddict_erase_f(Key, Val1, Dict) -> ddict_erase(Key, Val1, Dict, fun select_f/1). -ddict_erase_r(Key, Val2, Dict) -> ddict_erase(Key, Val2, Dict, fun select_r/1). - -ddict_erase(Key, ValX, Dict, Select) -> - case dict:find(Key, Dict) of - {ok, Dicts} -> - {DictX, DictY} = Select(Dicts), - Dicts1 = {D, _} = - case dict:find(ValX, DictX) of - {ok, ValY} -> Select({dict:erase(ValX, DictX), - dict:erase(ValY, DictY)}); - error -> Dicts - end, - case dict:size(D) of - 0 -> dict:erase(Key, Dict); - _ -> dict:store(Key, Dicts1, Dict) - end; - error -> - Dict - end. - -select_f({A, B}) -> {A, B}. -select_r({A, B}) -> {B, A}. -- cgit v1.2.1 From 01664aed1d4f49f0dccc9d2e29d36d5c1513e694 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 16 Aug 2013 16:03:19 +0100 Subject: ahem --- src/delegate.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 6e216cce..9642e9ca 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -230,7 +230,7 @@ handle_info({'DOWN', Ref, process, Pid, Info}, case dict:find(Pid, Monitors) of {ok, {Ref, Set}} -> sets:fold( - fun (WantsMonitor, none) -> + fun (WantsMonitor, _) -> WantsMonitor ! {'DOWN', {Name, Pid}, process, Pid, Info} end, none, Set), State#state{monitors = dict:erase(Pid, Monitors)}; -- cgit v1.2.1 From 26fe3d9ba9026015e39211916fa8a9e92df0671d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 17 Aug 2013 12:05:19 +0100 Subject: we only support process monitoring, so enforce that in the API --- src/delegate.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 9642e9ca..5ed31d08 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -124,11 +124,11 @@ invoke_no_result(Pids, FunOrMFA) when is_list(Pids) -> safe_invoke(LocalPids, FunOrMFA), %% must not die ok. -monitor(Type, Pid) when node(Pid) =:= node() -> - erlang:monitor(Type, Pid); -monitor(Type, Pid) -> +monitor(process, Pid) when node(Pid) =:= node() -> + erlang:monitor(process, Pid); +monitor(process, Pid) -> Name = delegate(Pid, [node(Pid)]), - gen_server2:cast(Name, {monitor, Type, self(), Pid}), + gen_server2:cast(Name, {monitor, self(), Pid}), {Name, Pid}. demonitor(Ref) -> ?MODULE:demonitor(Ref, []). @@ -192,14 +192,14 @@ handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) -> {reply, safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), State, hibernate}. -handle_cast({monitor, Type, WantsMonitor, Pid}, +handle_cast({monitor, WantsMonitor, Pid}, State = #state{monitors = Monitors}) -> Monitors1 = case dict:find(Pid, Monitors) of {ok, {Ref, Set}} -> Set1 = sets:add_element(WantsMonitor, Set), dict:store(Pid, {Ref, Set1}, Monitors); error -> - Ref = erlang:monitor(Type, Pid), + Ref = erlang:monitor(process, Pid), Set = sets:from_list([WantsMonitor]), dict:store(Pid, {Ref, Set}, Monitors) end, -- cgit v1.2.1 From 27486e20a3820d5913193cc00ccf19ea44e5d795 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 17 Aug 2013 12:28:30 +0100 Subject: we don't really support demonitor/2, so rip it out due to the aggregration of all monitors of a single pid into a single monitor, the semantics of demonitor/2 is decidely dodgy --- src/delegate.erl | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 5ed31d08..25260aad 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -18,8 +18,8 @@ -behaviour(gen_server2). --export([start_link/1, invoke_no_result/2, invoke/2, monitor/2, - demonitor/1, demonitor/2, call/2, cast/2]). +-export([start_link/1, invoke_no_result/2, invoke/2, + monitor/2, demonitor/1, call/2, cast/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -43,7 +43,6 @@ -spec(invoke_no_result/2 :: (pid() | [pid()], fun_or_mfa(any())) -> 'ok'). -spec(monitor/2 :: ('process', pid()) -> monitor_ref()). -spec(demonitor/1 :: (monitor_ref()) -> 'true'). --spec(demonitor/2 :: (monitor_ref(), ['flush']) -> 'true'). -spec(call/2 :: ( pid(), any()) -> any(); @@ -131,12 +130,10 @@ monitor(process, Pid) -> gen_server2:cast(Name, {monitor, self(), Pid}), {Name, Pid}. -demonitor(Ref) -> ?MODULE:demonitor(Ref, []). - -demonitor(Ref, Options) when is_reference(Ref) -> - erlang:demonitor(Ref, Options); -demonitor({Name, Pid}, Options) -> - gen_server2:cast(Name, {demonitor, self(), Pid, Options}). +demonitor(Ref) when is_reference(Ref) -> + erlang:demonitor(Ref); +demonitor({Name, Pid}) -> + gen_server2:cast(Name, {demonitor, self(), Pid}). call(PidOrPids, Msg) -> invoke(PidOrPids, {gen_server2, call, [Msg, infinity]}). @@ -205,13 +202,13 @@ handle_cast({monitor, WantsMonitor, Pid}, end, {noreply, State#state{monitors = Monitors1}, hibernate}; -handle_cast({demonitor, WantsMonitor, Pid, Options}, +handle_cast({demonitor, WantsMonitor, Pid}, State = #state{monitors = Monitors}) -> Monitors1 = case dict:find(Pid, Monitors) of {ok, {Ref, Set}} -> Set1 = sets:del_element(WantsMonitor, Set), case sets:size(Set1) of - 0 -> erlang:demonitor(Ref, Options), + 0 -> erlang:demonitor(Ref), dict:erase(Pid, Monitors); _ -> dict:store(Pid, {Ref, Set1}, Monitors) end; -- cgit v1.2.1 From 4c581d6ca51e709f687ce375573c8e840a8b6c7e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 17 Aug 2013 12:42:56 +0100 Subject: cosmetics, better var names, and optimise 'DOWN' sending --- src/delegate.erl | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 25260aad..50518ded 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -189,31 +189,31 @@ handle_call({invoke, FunOrMFA, Grouped}, _From, State = #state{node = Node}) -> {reply, safe_invoke(orddict:fetch(Node, Grouped), FunOrMFA), State, hibernate}. -handle_cast({monitor, WantsMonitor, Pid}, +handle_cast({monitor, MonitoringPid, Pid}, State = #state{monitors = Monitors}) -> Monitors1 = case dict:find(Pid, Monitors) of - {ok, {Ref, Set}} -> - Set1 = sets:add_element(WantsMonitor, Set), - dict:store(Pid, {Ref, Set1}, Monitors); + {ok, {Ref, Pids}} -> + Pids1 = sets:add_element(MonitoringPid, Pids), + dict:store(Pid, {Ref, Pids1}, Monitors); error -> Ref = erlang:monitor(process, Pid), - Set = sets:from_list([WantsMonitor]), - dict:store(Pid, {Ref, Set}, Monitors) + Pids = sets:from_list([MonitoringPid]), + dict:store(Pid, {Ref, Pids}, Monitors) end, {noreply, State#state{monitors = Monitors1}, hibernate}; -handle_cast({demonitor, WantsMonitor, Pid}, +handle_cast({demonitor, MonitoringPid, Pid}, State = #state{monitors = Monitors}) -> Monitors1 = case dict:find(Pid, Monitors) of - {ok, {Ref, Set}} -> - Set1 = sets:del_element(WantsMonitor, Set), - case sets:size(Set1) of - 0 -> erlang:demonitor(Ref), - dict:erase(Pid, Monitors); - _ -> dict:store(Pid, {Ref, Set1}, Monitors) - end; - error -> - Monitors + {ok, {Ref, Pids}} -> + Pids1 = sets:del_element(MonitoringPid, Pids), + case sets:size(Pids1) of + 0 -> erlang:demonitor(Ref), + dict:erase(Pid, Monitors); + _ -> dict:store(Pid, {Ref, Pids1}, Monitors) + end; + error -> + Monitors end, {noreply, State#state{monitors = Monitors1}, hibernate}; @@ -225,11 +225,10 @@ handle_info({'DOWN', Ref, process, Pid, Info}, State = #state{monitors = Monitors, name = Name}) -> {noreply, case dict:find(Pid, Monitors) of - {ok, {Ref, Set}} -> - sets:fold( - fun (WantsMonitor, _) -> - WantsMonitor ! {'DOWN', {Name, Pid}, process, Pid, Info} - end, none, Set), + {ok, {Ref, Pids}} -> + Msg = {'DOWN', {Name, Pid}, process, Pid, Info}, + sets:fold(fun (MonitoringPid, _) -> MonitoringPid ! Msg end, + none, Pids), State#state{monitors = dict:erase(Pid, Monitors)}; error -> State -- cgit v1.2.1 From 951619dc64b7ac1afd0072ca371ff96af55a9677 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Aug 2013 10:09:04 +0100 Subject: Use gb_sets rather than sets for performance. --- src/delegate.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index 50518ded..c10e7343 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -193,11 +193,11 @@ handle_cast({monitor, MonitoringPid, Pid}, State = #state{monitors = Monitors}) -> Monitors1 = case dict:find(Pid, Monitors) of {ok, {Ref, Pids}} -> - Pids1 = sets:add_element(MonitoringPid, Pids), + Pids1 = gb_sets:add_element(MonitoringPid, Pids), dict:store(Pid, {Ref, Pids1}, Monitors); error -> Ref = erlang:monitor(process, Pid), - Pids = sets:from_list([MonitoringPid]), + Pids = gb_sets:from_list([MonitoringPid]), dict:store(Pid, {Ref, Pids}, Monitors) end, {noreply, State#state{monitors = Monitors1}, hibernate}; @@ -206,8 +206,8 @@ handle_cast({demonitor, MonitoringPid, Pid}, State = #state{monitors = Monitors}) -> Monitors1 = case dict:find(Pid, Monitors) of {ok, {Ref, Pids}} -> - Pids1 = sets:del_element(MonitoringPid, Pids), - case sets:size(Pids1) of + Pids1 = gb_sets:del_element(MonitoringPid, Pids), + case gb_sets:size(Pids1) of 0 -> erlang:demonitor(Ref), dict:erase(Pid, Monitors); _ -> dict:store(Pid, {Ref, Pids1}, Monitors) @@ -227,8 +227,8 @@ handle_info({'DOWN', Ref, process, Pid, Info}, case dict:find(Pid, Monitors) of {ok, {Ref, Pids}} -> Msg = {'DOWN', {Name, Pid}, process, Pid, Info}, - sets:fold(fun (MonitoringPid, _) -> MonitoringPid ! Msg end, - none, Pids), + gb_sets:fold(fun (MonitoringPid, _) -> MonitoringPid ! Msg end, + none, Pids), State#state{monitors = dict:erase(Pid, Monitors)}; error -> State -- cgit v1.2.1 From be57137d957b2cff32f586192173ff6d59940925 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 19 Aug 2013 11:30:23 +0100 Subject: refactor: make proper use of gb_sets API --- src/delegate.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/delegate.erl b/src/delegate.erl index c10e7343..0331ca01 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -197,7 +197,7 @@ handle_cast({monitor, MonitoringPid, Pid}, dict:store(Pid, {Ref, Pids1}, Monitors); error -> Ref = erlang:monitor(process, Pid), - Pids = gb_sets:from_list([MonitoringPid]), + Pids = gb_sets:singleton(MonitoringPid), dict:store(Pid, {Ref, Pids}, Monitors) end, {noreply, State#state{monitors = Monitors1}, hibernate}; @@ -207,10 +207,10 @@ handle_cast({demonitor, MonitoringPid, Pid}, Monitors1 = case dict:find(Pid, Monitors) of {ok, {Ref, Pids}} -> Pids1 = gb_sets:del_element(MonitoringPid, Pids), - case gb_sets:size(Pids1) of - 0 -> erlang:demonitor(Ref), - dict:erase(Pid, Monitors); - _ -> dict:store(Pid, {Ref, Pids1}, Monitors) + case gb_sets:is_empty(Pids1) of + true -> erlang:demonitor(Ref), + dict:erase(Pid, Monitors); + false -> dict:store(Pid, {Ref, Pids1}, Monitors) end; error -> Monitors -- cgit v1.2.1 From 9a75e3de3ee409941a2dabf098aa56b334849419 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Aug 2013 17:44:31 +0100 Subject: Prevent exclusive queues from being durable or mirrored. --- src/rabbit_channel.erl | 3 ++- src/rabbit_mirror_queue_misc.erl | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d6c1e8c0..115849cc 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -961,7 +961,7 @@ handle_method(#'exchange.unbind'{destination = DestinationNameBin, handle_method(#'queue.declare'{queue = QueueNameBin, passive = false, - durable = Durable, + durable = DurableDeclare, exclusive = ExclusiveDeclare, auto_delete = AutoDelete, nowait = NoWait, @@ -973,6 +973,7 @@ handle_method(#'queue.declare'{queue = QueueNameBin, true -> ConnPid; false -> none end, + Durable = DurableDeclare andalso not ExclusiveDeclare, ActualNameBin = case QueueNameBin of <<>> -> rabbit_guid:binary(rabbit_guid:gen_secure(), "amq.gen"); diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index eded0411..a5a1d922 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -237,16 +237,20 @@ suggested_queue_nodes(Q) -> %% This variant exists so we can pull a call to %% rabbit_mnesia:cluster_nodes(running) out of a loop or %% transaction or both. -suggested_queue_nodes(Q, All) -> +suggested_queue_nodes(Q = #amqqueue{exclusive_owner = Owner}, All) -> {MNode0, SNodes, SSNodes} = actual_queue_nodes(Q), MNode = case MNode0 of none -> node(); _ -> MNode0 end, - Params = policy(<<"ha-params">>, Q), - case module(Q) of - {ok, M} -> M:suggested_queue_nodes(Params, MNode, SNodes, SSNodes, All); - _ -> {MNode, []} + case Owner of + none -> Params = policy(<<"ha-params">>, Q), + case module(Q) of + {ok, M} -> M:suggested_queue_nodes( + Params, MNode, SNodes, SSNodes, All); + _ -> {MNode, []} + end; + _ -> {MNode, []} end. policy(Policy, Q) -> -- cgit v1.2.1 From ebb60751b05ac08827eef253e4401919fdf8add3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Aug 2013 18:16:19 +0100 Subject: fix spec --- src/priority_queue.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 9b24f09f..ef45ef66 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -65,7 +65,8 @@ -spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}). -spec(join/2 :: (pqueue(), pqueue()) -> pqueue()). -spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()). --spec(fold/3 :: (fun ((any(), any()) -> any()), any(), pqueue()) -> any()). +-spec(fold/3 :: + (fun ((any(), priority(), any()) -> any()), any(), pqueue()) -> any()). -spec(highest/1 :: (pqueue()) -> priority() | 'empty'). -endif. -- cgit v1.2.1 From 81080c1b4388dae2420e6e32406d943f884a2e30 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 20 Aug 2013 15:22:18 +0100 Subject: Push everything through notify_decorators/3. --- src/rabbit_amqqueue_process.erl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e432f226..4d8e1c19 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -198,7 +198,7 @@ declare(Recover, From, State = #q{q = Q, recovery_barrier(Recover), State1 = process_args(State#q{backing_queue = BQ, backing_queue_state = BQS}), - callback(qname(State), startup, []), + notify_decorators(startup, [], State), rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State1)), rabbit_event:if_enabled(State1, #q.stats_timer, @@ -223,7 +223,19 @@ matches(new, Q1, Q2) -> matches(_, Q, Q) -> true; matches(_, _Q, _Q1) -> false. -callback(QName, F, A) -> +notify_decorators(Event, Props, State) when Event =:= startup; + Event =:= shutdown -> + decorator_callback(qname(State), Event, Props); + +notify_decorators(Event, Props, State = #q{active_consumers = ACs, + backing_queue = BQ, + backing_queue_state = BQS}) -> + decorator_callback( + qname(State), notify, + [Event, [{max_active_consumer_priority, priority_queue:highest(ACs)}, + {is_empty, BQ:is_empty(BQS)} | Props]]). + +decorator_callback(QName, F, A) -> %% Look up again in case policy and hence decorators have changed case rabbit_amqqueue:lookup(QName) of {ok, Q = #amqqueue{decorators = Ds}} -> @@ -232,14 +244,6 @@ callback(QName, F, A) -> ok end. -notify_decorators(Event, Props, State = #q{active_consumers = ACs, - backing_queue = BQ, - backing_queue_state = BQS}) -> - callback(qname(State), notify, - [Event, - [{max_active_consumer_priority, priority_queue:highest(ACs)}, - {is_empty, BQ:is_empty(BQS)} | Props]]). - bq_init(BQ, Q, Recover) -> Self = self(), BQ:init(Q, Recover =/= new, @@ -293,7 +297,7 @@ terminate_shutdown(Fun, State) -> undefined -> State1; _ -> ok = rabbit_memory_monitor:deregister(self()), QName = qname(State), - callback(QName, shutdown, []), + notify_decorators(shutdown, [], State), [emit_consumer_deleted(Ch, CTag, QName) || {Ch, CTag, _} <- consumers(State1)], State1#q{backing_queue_state = Fun(BQS)} -- cgit v1.2.1 From e8237d531a62b7ad5b787fcf8c301082ed507917 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 20 Aug 2013 15:29:46 +0100 Subject: Tighter and slightly more self-documenting. --- src/rabbit_queue_decorator.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_queue_decorator.erl b/src/rabbit_queue_decorator.erl index d3dec9dc..a0d0cca5 100644 --- a/src/rabbit_queue_decorator.erl +++ b/src/rabbit_queue_decorator.erl @@ -8,6 +8,13 @@ -ifdef(use_specs). +-type(notify_event() :: 'consumer_blocked' | + 'consumer_unblocked' | + 'queue_run_finished' | + 'basic_consume' | + 'basic_cancel' | + 'notification_requested'). + -callback startup(rabbit_types:amqqueue()) -> 'ok'. -callback shutdown(rabbit_types:amqqueue()) -> 'ok'. @@ -17,7 +24,7 @@ -callback active_for(rabbit_types:amqqueue()) -> boolean(). --callback notify(rabbit_types:amqqueue(), atom(), any()) -> 'ok'. +-callback notify(rabbit_types:amqqueue(), notify_event(), any()) -> 'ok'. -else. -- cgit v1.2.1 From 4c602c8642924d966738eebb1e05b906d43ffec9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Aug 2013 11:09:34 +0100 Subject: Small changes: Don't reevaluate consumer priority on every delivery, refactor a touch, realign, fix test, more specific spec. --- src/priority_queue.erl | 5 +++-- src/rabbit_amqqueue_process.erl | 29 ++++++++++++++--------------- src/rabbit_tests.erl | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index ef45ef66..c76c0d33 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -41,7 +41,7 @@ -module(priority_queue). -export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, from_list/1, - in/2, in/3, out/1, join/2, filter/2, fold/3, highest/1]). + in/2, in/3, out/1, out_p/1, join/2, filter/2, fold/3, highest/1]). %%---------------------------------------------------------------------------- @@ -63,10 +63,11 @@ -spec(in/2 :: (any(), pqueue()) -> pqueue()). -spec(in/3 :: (any(), priority(), pqueue()) -> pqueue()). -spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}). +-spec(out_p/1 :: (pqueue()) -> {empty | {value, any(), priority()}, pqueue()}). -spec(join/2 :: (pqueue(), pqueue()) -> pqueue()). -spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()). -spec(fold/3 :: - (fun ((any(), priority(), any()) -> any()), any(), pqueue()) -> any()). + (fun ((any(), priority(), A) -> A), A, pqueue()) -> A). -spec(highest/1 :: (pqueue()) -> priority() | 'empty'). -endif. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4d8e1c19..5ddd14a9 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -430,7 +430,7 @@ all_ch_record() -> [C || {{ch, _}, C} <- get()]. block_consumer(C = #cr{blocked_consumers = Blocked}, {_ChPid, #consumer{tag = CTag}} = QEntry, State) -> - Blocked1 = priority_queue:in(QEntry, consumer_priority(QEntry), Blocked), + Blocked1 = add_consumer(QEntry, Blocked), update_ch_record(C#cr{blocked_consumers = Blocked1}), notify_decorators(consumer_blocked, [{consumer_tag, CTag}], State). @@ -456,17 +456,17 @@ deliver_msgs_to_consumers(_DeliverFun, true, State) -> {true, State}; deliver_msgs_to_consumers(DeliverFun, false, State = #q{active_consumers = ActiveConsumers}) -> - case priority_queue:out(ActiveConsumers) of + case priority_queue:out_p(ActiveConsumers) of {empty, _} -> {false, State}; - {{value, QEntry}, Tail} -> + {{value, QEntry, Priority}, Tail} -> {Stop, State1} = deliver_msg_to_consumer( - DeliverFun, QEntry, + DeliverFun, {QEntry, Priority}, State#q{active_consumers = Tail}), deliver_msgs_to_consumers(DeliverFun, Stop, State1) end. -deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> +deliver_msg_to_consumer(DeliverFun, {E = {ChPid, Consumer}, Priority}, State) -> C = lookup_ch(ChPid), case is_ch_blocked(C) of true -> block_consumer(C, E, State), @@ -478,7 +478,7 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> block_consumer(C#cr{limiter = Limiter}, E, State), {false, State}; {continue, Limiter} -> - AC1 = priority_queue:in(E, consumer_priority(E), + AC1 = priority_queue:in(E, Priority, State#q.active_consumers), deliver_msg_to_consumer( DeliverFun, Consumer, C#cr{limiter = Limiter}, @@ -561,11 +561,12 @@ run_message_queue(State) -> notify_decorators(queue_run_finished, [], State1), State1. -consumer_priority({_ChPid, #consumer{args = Args}}) -> - case rabbit_misc:table_lookup(Args, <<"x-priority">>) of - {_, Priority} -> Priority; - _ -> 0 - end. +add_consumer({ChPid, Consumer = #consumer{args = Args}}, ActiveConsumers) -> + Priority = case rabbit_misc:table_lookup(Args, <<"x-priority">>) of + {_, P} -> P; + _ -> 0 + end, + priority_queue:in({ChPid, Consumer}, Priority, ActiveConsumers). attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, Props, Delivered, State = #q{backing_queue = BQ, @@ -1195,7 +1196,7 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, true -> send_drained(C1); false -> ok end, - Consumer = #consumer{tag = ConsumerTag, + Consumer = #consumer{tag = ConsumerTag, ack_required = not NoAck, args = OtherArgs}, ExclusiveConsumer = if ExclusiveConsume -> {ChPid, ConsumerTag}; @@ -1206,9 +1207,7 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, ok = maybe_send_reply(ChPid, OkMsg), emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, not NoAck, qname(State1)), - AC1 = priority_queue:in({ChPid, Consumer}, - consumer_priority({ChPid, Consumer}), - State1#q.active_consumers), + AC1 = add_consumer({ChPid, Consumer}, State1#q.active_consumers), State2 = State1#q{active_consumers = AC1}, notify_decorators( basic_consume, [{consumer_tag, ConsumerTag}], State2), diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 5af4969a..76421d1a 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1168,7 +1168,7 @@ test_server_status() -> rabbit_misc:r(<<"/">>, queue, Name), false, false, [], none)]], ok = rabbit_amqqueue:basic_consume( - Q, true, Ch, Limiter, false, <<"ctag">>, true, none, undefined), + Q, true, Ch, Limiter, false, <<"ctag">>, true, none, [], undefined), %% list queues ok = info_action(list_queues, rabbit_amqqueue:info_keys(), true), -- cgit v1.2.1 From 847153c5a3351fa3f01cd8a906d73adaabfe0aca Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Aug 2013 12:11:22 +0100 Subject: Slightly clearer? --- src/rabbit_amqqueue_process.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5ddd14a9..e8011133 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -461,12 +461,12 @@ deliver_msgs_to_consumers(DeliverFun, false, {false, State}; {{value, QEntry, Priority}, Tail} -> {Stop, State1} = deliver_msg_to_consumer( - DeliverFun, {QEntry, Priority}, + DeliverFun, QEntry, Priority, State#q{active_consumers = Tail}), deliver_msgs_to_consumers(DeliverFun, Stop, State1) end. -deliver_msg_to_consumer(DeliverFun, {E = {ChPid, Consumer}, Priority}, State) -> +deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, Priority, State) -> C = lookup_ch(ChPid), case is_ch_blocked(C) of true -> block_consumer(C, E, State), @@ -480,19 +480,19 @@ deliver_msg_to_consumer(DeliverFun, {E = {ChPid, Consumer}, Priority}, State) -> {continue, Limiter} -> AC1 = priority_queue:in(E, Priority, State#q.active_consumers), - deliver_msg_to_consumer( + deliver_msg_to_consumer0( DeliverFun, Consumer, C#cr{limiter = Limiter}, State#q{active_consumers = AC1}) end end. -deliver_msg_to_consumer(DeliverFun, - #consumer{tag = ConsumerTag, - ack_required = AckRequired}, - C = #cr{ch_pid = ChPid, - acktags = ChAckTags, - unsent_message_count = Count}, - State = #q{q = #amqqueue{name = QName}}) -> +deliver_msg_to_consumer0(DeliverFun, + #consumer{tag = ConsumerTag, + ack_required = AckRequired}, + C = #cr{ch_pid = ChPid, + acktags = ChAckTags, + unsent_message_count = Count}, + State = #q{q = #amqqueue{name = QName}}) -> {{Message, IsDelivered, AckTag}, Stop, State1} = DeliverFun(AckRequired, State), rabbit_channel:deliver(ChPid, ConsumerTag, AckRequired, -- cgit v1.2.1 From 1e05290def77161bafa0659a632626f2c958bd08 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Aug 2013 13:21:35 +0100 Subject: Notify "startup" when we fail over too, that means the federation links will fail over with us. --- src/rabbit_amqqueue_process.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e8011133..b15c9149 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -137,9 +137,11 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, senders = Senders, msg_id_to_channel = MTC}, State2 = process_args(State1), - lists:foldl(fun (Delivery, StateN) -> - deliver_or_enqueue(Delivery, true, StateN) - end, State2, Deliveries). + State3 = lists:foldl(fun (Delivery, StateN) -> + deliver_or_enqueue(Delivery, true, StateN) + end, State2, Deliveries), + notify_decorators(startup, [], State3), + State3. init_state(Q) -> State = #q{q = Q, -- cgit v1.2.1 From ba3313ec9d8c0460786b79992c4c37a2619fb5fb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Aug 2013 15:25:46 +0100 Subject: Rename notification_requested to refresh, and emit queue_empty rather than queue_run_finished - we don't care if the queue run has finished and we are not empty, but we *do* care if we have become empty by some other method (purge, TTL). --- src/rabbit_amqqueue_process.erl | 6 +++--- src/rabbit_queue_decorator.erl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b15c9149..3167ffe5 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -441,7 +441,8 @@ is_ch_blocked(#cr{unsent_message_count = Count, limiter = Limiter}) -> maybe_send_drained(WasEmpty, State) -> case (not WasEmpty) andalso is_empty(State) of - true -> [send_drained(C) || C <- all_ch_record()]; + true -> notify_decorators(queue_empty, [], State), + [send_drained(C) || C <- all_ch_record()]; false -> ok end, State. @@ -560,7 +561,6 @@ run_message_queue(State) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, is_empty(State), State), - notify_decorators(queue_run_finished, [], State1), State1. add_consumer({ChPid, Consumer = #consumer{args = Args}}, ActiveConsumers) -> @@ -1422,7 +1422,7 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, end); handle_cast(notify_decorators, State) -> - notify_decorators(notification_requested, [], State), + notify_decorators(refresh, [], State), noreply(State); handle_cast(wake_up, State) -> diff --git a/src/rabbit_queue_decorator.erl b/src/rabbit_queue_decorator.erl index a0d0cca5..8f6375a5 100644 --- a/src/rabbit_queue_decorator.erl +++ b/src/rabbit_queue_decorator.erl @@ -10,10 +10,10 @@ -type(notify_event() :: 'consumer_blocked' | 'consumer_unblocked' | - 'queue_run_finished' | + 'queue_empty' | 'basic_consume' | 'basic_cancel' | - 'notification_requested'). + 'refresh'). -callback startup(rabbit_types:amqqueue()) -> 'ok'. -- cgit v1.2.1 From c153e81488594754505bfab34d1409781cb972e9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Aug 2013 15:50:27 +0100 Subject: Transplant consumer priorities from bug 25553. --- src/priority_queue.erl | 37 +++++++++++++++- src/rabbit_amqqueue.erl | 11 ++--- src/rabbit_amqqueue_process.erl | 93 +++++++++++++++++++++++------------------ src/rabbit_channel.erl | 17 ++++---- src/rabbit_tests.erl | 2 +- 5 files changed, 103 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 6995c3be..c76c0d33 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -40,8 +40,8 @@ -module(priority_queue). --export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, in/2, in/3, - out/1, join/2]). +-export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, from_list/1, + in/2, in/3, out/1, out_p/1, join/2, filter/2, fold/3, highest/1]). %%---------------------------------------------------------------------------- @@ -59,10 +59,16 @@ -spec(is_empty/1 :: (pqueue()) -> boolean()). -spec(len/1 :: (pqueue()) -> non_neg_integer()). -spec(to_list/1 :: (pqueue()) -> [{priority(), any()}]). +-spec(from_list/1 :: ([{priority(), any()}]) -> pqueue()). -spec(in/2 :: (any(), pqueue()) -> pqueue()). -spec(in/3 :: (any(), priority(), pqueue()) -> pqueue()). -spec(out/1 :: (pqueue()) -> {empty | {value, any()}, pqueue()}). +-spec(out_p/1 :: (pqueue()) -> {empty | {value, any(), priority()}, pqueue()}). -spec(join/2 :: (pqueue(), pqueue()) -> pqueue()). +-spec(filter/2 :: (fun ((any()) -> boolean()), pqueue()) -> pqueue()). +-spec(fold/3 :: + (fun ((any(), priority(), A) -> A), A, pqueue()) -> A). +-spec(highest/1 :: (pqueue()) -> priority() | 'empty'). -endif. @@ -96,6 +102,9 @@ to_list({pqueue, Queues}) -> [{maybe_negate_priority(P), V} || {P, Q} <- Queues, {0, V} <- to_list(Q)]. +from_list(L) -> + lists:foldl(fun ({P, E}, Q) -> in(E, P, Q) end, new(), L). + in(Item, Q) -> in(Item, 0, Q). @@ -147,6 +156,14 @@ out({pqueue, [{P, Q} | Queues]}) -> end, {R, NewQ}. +out_p({queue, _, _, _} = Q) -> add_p(out(Q), 0); +out_p({pqueue, [{P, _} | _]} = Q) -> add_p(out(Q), maybe_negate_priority(P)). + +add_p(R, P) -> case R of + {empty, Q} -> {empty, Q}; + {{value, V}, Q} -> {{value, V, P}, Q} + end. + join(A, {queue, [], [], 0}) -> A; join({queue, [], [], 0}, B) -> @@ -185,6 +202,22 @@ merge([{PA, A}|As], Bs = [{PB, _}|_], Acc) when PA < PB orelse PA == infinity -> merge(As = [{_, _}|_], [{PB, B}|Bs], Acc) -> merge(As, Bs, [ {PB, B} | Acc ]). +filter(Pred, Q) -> fold(fun(V, P, Acc) -> + case Pred(V) of + true -> in(V, P, Acc); + false -> Acc + end + end, new(), Q). + +fold(Fun, Init, Q) -> case out_p(Q) of + {empty, _Q} -> Init; + {{value, V, P}, Q1} -> fold(Fun, Fun(V, P, Init), Q1) + end. + +highest({queue, [], [], 0}) -> empty; +highest({queue, _, _, _}) -> 0; +highest({pqueue, [{P, _} | _]}) -> maybe_negate_priority(P). + r2f([], 0) -> {queue, [], [], 0}; r2f([_] = R, 1) -> {queue, [], R, 1}; r2f([X,Y], 2) -> {queue, [X], [Y], 2}; diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index a1efaf65..0ec1cd54 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -26,7 +26,7 @@ -export([list/0, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). -export([force_event_refresh/0, wake_up/1]). -export([consumers/1, consumers_all/1, consumer_info_keys/0]). --export([basic_get/4, basic_consume/9, basic_cancel/4]). +-export([basic_get/4, basic_consume/10, basic_cancel/4]). -export([notify_sent/2, notify_sent_queue_down/1, resume/2, flush_all/2]). -export([notify_down_all/2, activate_limit_all/2, credit/5]). -export([on_node_down/1]). @@ -149,9 +149,9 @@ {'ok', non_neg_integer(), qmsg()} | 'empty'). -spec(credit/5 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), non_neg_integer(), boolean()) -> 'ok'). --spec(basic_consume/9 :: +-spec(basic_consume/10 :: (rabbit_types:amqqueue(), boolean(), pid(), pid(), boolean(), - rabbit_types:ctag(), boolean(), {non_neg_integer(), boolean()} | 'none', any()) + rabbit_types:ctag(), boolean(), {non_neg_integer(), boolean()} | 'none', any(), any()) -> rabbit_types:ok_or_error('exclusive_consume_unavailable')). -spec(basic_cancel/4 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok'). @@ -550,9 +550,10 @@ basic_get(#amqqueue{pid = QPid}, ChPid, NoAck, LimiterPid) -> delegate:call(QPid, {basic_get, ChPid, NoAck, LimiterPid}). basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, LimiterPid, LimiterActive, - ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg) -> + ConsumerTag, ExclusiveConsume, CreditArgs, OtherArgs, OkMsg) -> delegate:call(QPid, {basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, - ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg}). + ConsumerTag, ExclusiveConsume, CreditArgs, OtherArgs, + OkMsg}). basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) -> delegate:call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6e0eb9bf..547efa45 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -55,7 +55,7 @@ status }). --record(consumer, {tag, ack_required}). +-record(consumer, {tag, ack_required, args}). %% These are held in our process dictionary -record(cr, {ch_pid, @@ -145,7 +145,7 @@ init_state(Q) -> State = #q{q = Q, exclusive_consumer = none, has_had_consumers = false, - active_consumers = queue:new(), + active_consumers = priority_queue:new(), senders = pmon:new(delegate), msg_id_to_channel = gb_trees:empty(), status = running}, @@ -358,7 +358,7 @@ ensure_stats_timer(State) -> rabbit_event:ensure_stats_timer(State, #q.stats_timer, emit_stats). assert_invariant(State = #q{active_consumers = AC}) -> - true = (queue:is_empty(AC) orelse is_empty(State)). + true = (priority_queue:is_empty(AC) orelse is_empty(State)). is_empty(#q{backing_queue = BQ, backing_queue_state = BQS}) -> BQ:is_empty(BQS). @@ -377,7 +377,7 @@ ch_record(ChPid, LimiterPid) -> monitor_ref = MonitorRef, acktags = queue:new(), consumer_count = 0, - blocked_consumers = queue:new(), + blocked_consumers = priority_queue:new(), limiter = Limiter, unsent_message_count = 0}, put(Key, C), @@ -406,7 +406,7 @@ erase_ch_record(#cr{ch_pid = ChPid, monitor_ref = MonitorRef}) -> all_ch_record() -> [C || {{ch, _}, C} <- get()]. block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry) -> - update_ch_record(C#cr{blocked_consumers = queue:in(QEntry, Blocked)}). + update_ch_record(C#cr{blocked_consumers = add_consumer(QEntry, Blocked)}). is_ch_blocked(#cr{unsent_message_count = Count, limiter = Limiter}) -> Count >= ?UNSENT_MESSAGE_LIMIT orelse rabbit_limiter:is_suspended(Limiter). @@ -430,17 +430,17 @@ deliver_msgs_to_consumers(_DeliverFun, true, State) -> {true, State}; deliver_msgs_to_consumers(DeliverFun, false, State = #q{active_consumers = ActiveConsumers}) -> - case queue:out(ActiveConsumers) of + case priority_queue:out_p(ActiveConsumers) of {empty, _} -> {false, State}; - {{value, QEntry}, Tail} -> + {{value, QEntry, Priority}, Tail} -> {Stop, State1} = deliver_msg_to_consumer( - DeliverFun, QEntry, + DeliverFun, QEntry, Priority, State#q{active_consumers = Tail}), deliver_msgs_to_consumers(DeliverFun, Stop, State1) end. -deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> +deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, Priority, State) -> C = lookup_ch(ChPid), case is_ch_blocked(C) of true -> block_consumer(C, E), @@ -452,20 +452,21 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> block_consumer(C#cr{limiter = Limiter}, E), {false, State}; {continue, Limiter} -> - AC1 = queue:in(E, State#q.active_consumers), - deliver_msg_to_consumer( + AC1 = priority_queue:in(E, Priority, + State#q.active_consumers), + deliver_msg_to_consumer0( DeliverFun, Consumer, C#cr{limiter = Limiter}, State#q{active_consumers = AC1}) end end. -deliver_msg_to_consumer(DeliverFun, - #consumer{tag = ConsumerTag, - ack_required = AckRequired}, - C = #cr{ch_pid = ChPid, - acktags = ChAckTags, - unsent_message_count = Count}, - State = #q{q = #amqqueue{name = QName}}) -> +deliver_msg_to_consumer0(DeliverFun, + #consumer{tag = ConsumerTag, + ack_required = AckRequired}, + C = #cr{ch_pid = ChPid, + acktags = ChAckTags, + unsent_message_count = Count}, + State = #q{q = #amqqueue{name = QName}}) -> {{Message, IsDelivered, AckTag}, Stop, State1} = DeliverFun(AckRequired, State), rabbit_channel:deliver(ChPid, ConsumerTag, AckRequired, @@ -533,6 +534,13 @@ run_message_queue(State) -> is_empty(State), State), State1. +add_consumer({ChPid, Consumer = #consumer{args = Args}}, ActiveConsumers) -> + Priority = case rabbit_misc:table_lookup(Args, <<"x-priority">>) of + {_, P} -> P; + _ -> 0 + end, + priority_queue:in({ChPid, Consumer}, Priority, ActiveConsumers). + attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, Props, Delivered, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -629,17 +637,17 @@ requeue(AckTags, ChPid, State) -> fun (State1) -> requeue_and_run(AckTags, State1) end). remove_consumer(ChPid, ConsumerTag, Queue) -> - queue:filter(fun ({CP, #consumer{tag = CTag}}) -> - (CP /= ChPid) or (CTag /= ConsumerTag) - end, Queue). + priority_queue:filter(fun ({CP, #consumer{tag = CTag}}) -> + (CP /= ChPid) or (CTag /= ConsumerTag) + end, Queue). remove_consumers(ChPid, Queue, QName) -> - queue:filter(fun ({CP, #consumer{tag = CTag}}) when CP =:= ChPid -> - emit_consumer_deleted(ChPid, CTag, QName), - false; - (_) -> - true - end, Queue). + priority_queue:filter(fun ({CP, #consumer{tag = CTag}}) when CP =:= ChPid -> + emit_consumer_deleted(ChPid, CTag, QName), + false; + (_) -> + true + end, Queue). possibly_unblock(State, ChPid, Update) -> case lookup_ch(ChPid) of @@ -654,18 +662,19 @@ possibly_unblock(State, ChPid, Update) -> unblock(State, C = #cr{limiter = Limiter}) -> case lists:partition( - fun({_ChPid, #consumer{tag = CTag}}) -> + fun({_P, {_ChPid, #consumer{tag = CTag}}}) -> rabbit_limiter:is_consumer_blocked(Limiter, CTag) - end, queue:to_list(C#cr.blocked_consumers)) of + end, priority_queue:to_list(C#cr.blocked_consumers)) of {_, []} -> update_ch_record(C), State; {Blocked, Unblocked} -> - BlockedQ = queue:from_list(Blocked), - UnblockedQ = queue:from_list(Unblocked), + BlockedQ = priority_queue:from_list(Blocked), + UnblockedQ = priority_queue:from_list(Unblocked), update_ch_record(C#cr{blocked_consumers = BlockedQ}), - AC1 = queue:join(State#q.active_consumers, UnblockedQ), - run_message_queue(State#q{active_consumers = AC1}) + AC1 = priority_queue:join(State#q.active_consumers, UnblockedQ), + State1 = State#q{active_consumers = AC1}, + run_message_queue(State1) end. should_auto_delete(#q{q = #amqqueue{auto_delete = false}}) -> false; @@ -1002,9 +1011,9 @@ consumers(#q{active_consumers = ActiveConsumers}) -> consumers(ActiveConsumers, []), all_ch_record()). consumers(Consumers, Acc) -> - rabbit_misc:queue_fold( - fun ({ChPid, #consumer{tag = CTag, ack_required = AckRequired}}, Acc1) -> - [{ChPid, CTag, AckRequired} | Acc1] + priority_queue:fold( + fun ({ChPid, #consumer{tag = CTag, ack_required = AckReq}}, _P, Acc1) -> + [{ChPid, CTag, AckReq} | Acc1] end, Acc, Consumers). emit_stats(State) -> @@ -1134,7 +1143,7 @@ handle_call({basic_get, ChPid, NoAck, LimiterPid}, _From, end; handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, - ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg}, + ConsumerTag, ExclusiveConsume, CreditArgs, OtherArgs, OkMsg}, _From, State = #q{exclusive_consumer = Holder}) -> case check_exclusive_access(Holder, ExclusiveConsume, State) of in_use -> @@ -1157,8 +1166,9 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, true -> send_drained(C1); false -> ok end, - Consumer = #consumer{tag = ConsumerTag, - ack_required = not NoAck}, + Consumer = #consumer{tag = ConsumerTag, + ack_required = not NoAck, + args = OtherArgs}, ExclusiveConsumer = if ExclusiveConsume -> {ChPid, ConsumerTag}; true -> Holder end, @@ -1167,8 +1177,9 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, ok = maybe_send_reply(ChPid, OkMsg), emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, not NoAck, qname(State1)), - AC1 = queue:in({ChPid, Consumer}, State1#q.active_consumers), - reply(ok, run_message_queue(State1#q{active_consumers = AC1})) + AC1 = add_consumer({ChPid, Consumer}, State1#q.active_consumers), + State2 = State1#q{active_consumers = AC1}, + reply(ok, run_message_queue(State2)) end; handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, _From, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2cfbd96d..c3780581 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -734,7 +734,7 @@ handle_method(#'basic.consume'{queue = QueueNameBin, no_ack = NoAck, exclusive = ExclusiveConsume, nowait = NoWait, - arguments = Arguments}, + arguments = Args}, _, State = #ch{conn_pid = ConnPid, limiter = Limiter, consumer_mapping = ConsumerMapping}) -> @@ -755,12 +755,13 @@ handle_method(#'basic.consume'{queue = QueueNameBin, case rabbit_amqqueue:with_exclusive_access_or_die( QueueName, ConnPid, fun (Q) -> + {CreditArgs, OtherArgs} = parse_credit_args(Args), {rabbit_amqqueue:basic_consume( Q, NoAck, self(), rabbit_limiter:pid(Limiter), rabbit_limiter:is_active(Limiter), ActualConsumerTag, ExclusiveConsume, - parse_credit_args(Arguments), + CreditArgs, OtherArgs, ok_msg(NoWait, #'basic.consume_ok'{ consumer_tag = ActualConsumerTag})), Q} @@ -1247,12 +1248,12 @@ handle_delivering_queue_down(QPid, State = #ch{delivering_queues = DQ}) -> parse_credit_args(Arguments) -> case rabbit_misc:table_lookup(Arguments, <<"x-credit">>) of - {table, T} -> case {rabbit_misc:table_lookup(T, <<"credit">>), - rabbit_misc:table_lookup(T, <<"drain">>)} of - {{long, Credit}, {boolean, Drain}} -> {Credit, Drain}; - _ -> none - end; - undefined -> none + {table, T} -> {case {rabbit_misc:table_lookup(T, <<"credit">>), + rabbit_misc:table_lookup(T, <<"drain">>)} of + {{long, Credit}, {bool, Drain}} -> {Credit, Drain}; + _ -> none + end, lists:keydelete(<<"x-credit">>, 1, Arguments)}; + undefined -> {none, Arguments} end. binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 5af4969a..76421d1a 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1168,7 +1168,7 @@ test_server_status() -> rabbit_misc:r(<<"/">>, queue, Name), false, false, [], none)]], ok = rabbit_amqqueue:basic_consume( - Q, true, Ch, Limiter, false, <<"ctag">>, true, none, undefined), + Q, true, Ch, Limiter, false, <<"ctag">>, true, none, [], undefined), %% list queues ok = info_action(list_queues, rabbit_amqqueue:info_keys(), true), -- cgit v1.2.1 From 968d65b72c0734b7552aad7225e33646847786c6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Aug 2013 16:36:43 +0100 Subject: Consume arguments validation. --- src/rabbit_amqqueue.erl | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 0ec1cd54..10f97afd 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -393,9 +393,15 @@ with_exclusive_access_or_die(Name, ReaderPid, F) -> assert_args_equivalence(#amqqueue{name = QueueName, arguments = Args}, RequiredArgs) -> rabbit_misc:assert_args_equivalence(Args, RequiredArgs, QueueName, - [Key || {Key, _Fun} <- args()]). + [Key || {Key, _Fun} <- declare_args()]). check_declare_arguments(QueueName, Args) -> + check_arguments(QueueName, Args, declare_args()). + +check_consume_arguments(QueueName, Args) -> + check_arguments(QueueName, Args, consume_args()). + +check_arguments(QueueName, Args, Validators) -> [case rabbit_misc:table_lookup(Args, Key) of undefined -> ok; TypeVal -> case Fun(TypeVal, Args) of @@ -406,15 +412,17 @@ check_declare_arguments(QueueName, Args) -> [Key, rabbit_misc:rs(QueueName), Error]) end - end || {Key, Fun} <- args()], + end || {Key, Fun} <- Validators], ok. -args() -> +declare_args() -> [{<<"x-expires">>, fun check_expires_arg/2}, {<<"x-message-ttl">>, fun check_message_ttl_arg/2}, {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}, {<<"x-max-length">>, fun check_max_length_arg/2}]. +consume_args() -> [{<<"x-priority">>, fun check_int_arg/2}]. + check_int_arg({Type, _}, _) -> case lists:member(Type, ?INTEGER_ARG_TYPES) of true -> ok; @@ -549,8 +557,10 @@ credit(#amqqueue{pid = QPid}, ChPid, CTag, Credit, Drain) -> basic_get(#amqqueue{pid = QPid}, ChPid, NoAck, LimiterPid) -> delegate:call(QPid, {basic_get, ChPid, NoAck, LimiterPid}). -basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, LimiterPid, LimiterActive, +basic_consume(#amqqueue{pid = QPid, name = QName}, NoAck, ChPid, + LimiterPid, LimiterActive, ConsumerTag, ExclusiveConsume, CreditArgs, OtherArgs, OkMsg) -> + ok = check_consume_arguments(QName, OtherArgs), delegate:call(QPid, {basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, ConsumerTag, ExclusiveConsume, CreditArgs, OtherArgs, OkMsg}). -- cgit v1.2.1 From 5a38b5e837c68ea8c86acd3edc2bc5b932271b5f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Aug 2013 17:05:03 +0100 Subject: Add consumer arguments to events and rabbitmqctl. --- src/rabbit_amqqueue.erl | 6 +++--- src/rabbit_amqqueue_process.erl | 23 +++++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 10f97afd..0673ff8e 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -184,7 +184,7 @@ %%---------------------------------------------------------------------------- -define(CONSUMER_INFO_KEYS, - [queue_name, channel_pid, consumer_tag, ack_required]). + [queue_name, channel_pid, consumer_tag, ack_required, arguments]). recover() -> %% Clear out remnants of old incarnation, in case we restarted @@ -512,8 +512,8 @@ consumers_all(VHostPath) -> map(VHostPath, fun (Q) -> [lists:zip(ConsumerInfoKeys, - [Q#amqqueue.name, ChPid, ConsumerTag, AckRequired]) || - {ChPid, ConsumerTag, AckRequired} <- consumers(Q)] + [Q#amqqueue.name, ChPid, CTag, AckRequired, Args]) || + {ChPid, CTag, AckRequired, Args} <- consumers(Q)] end)). stat(#amqqueue{pid = QPid}) -> delegate:call(QPid, stat). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 547efa45..972e6be0 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1012,8 +1012,9 @@ consumers(#q{active_consumers = ActiveConsumers}) -> consumers(Consumers, Acc) -> priority_queue:fold( - fun ({ChPid, #consumer{tag = CTag, ack_required = AckReq}}, _P, Acc1) -> - [{ChPid, CTag, AckReq} | Acc1] + fun ({ChPid, Consumer}, _P, Acc1) -> + #consumer{tag = CTag, ack_required = Ack, args = Args} = Consumer, + [{ChPid, CTag, Ack, Args} | Acc1] end, Acc, Consumers). emit_stats(State) -> @@ -1022,13 +1023,14 @@ emit_stats(State) -> emit_stats(State, Extra) -> rabbit_event:notify(queue_stats, Extra ++ infos(?STATISTICS_KEYS, State)). -emit_consumer_created(ChPid, ConsumerTag, Exclusive, AckRequired, QName) -> +emit_consumer_created(ChPid, CTag, Exclusive, AckRequired, QName, Args) -> rabbit_event:notify(consumer_created, - [{consumer_tag, ConsumerTag}, + [{consumer_tag, CTag}, {exclusive, Exclusive}, {ack_required, AckRequired}, {channel, ChPid}, - {queue, QName}]). + {queue, QName}, + {arguments, Args}]). emit_consumer_deleted(ChPid, ConsumerTag, QName) -> rabbit_event:notify(consumer_deleted, @@ -1176,7 +1178,7 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, exclusive_consumer = ExclusiveConsumer}, ok = maybe_send_reply(ChPid, OkMsg), emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, - not NoAck, qname(State1)), + not NoAck, qname(State1), OtherArgs), AC1 = add_consumer({ChPid, Consumer}, State1#q.active_consumers), State2 = State1#q{active_consumers = AC1}, reply(ok, run_message_queue(State2)) @@ -1275,10 +1277,11 @@ handle_call(force_event_refresh, _From, QName = qname(State), case Exclusive of none -> [emit_consumer_created( - Ch, CTag, false, AckRequired, QName) || - {Ch, CTag, AckRequired} <- consumers(State)]; - {Ch, CTag} -> [{Ch, CTag, AckRequired}] = consumers(State), - emit_consumer_created(Ch, CTag, true, AckRequired, QName) + Ch, CTag, false, AckRequired, QName, Args) || + {Ch, CTag, AckRequired, Args} <- consumers(State)]; + {Ch, CTag} -> [{Ch, CTag, AckRequired, Args}] = consumers(State), + emit_consumer_created( + Ch, CTag, true, AckRequired, QName, Args) end, reply(ok, State). -- cgit v1.2.1 From 4693167d61f11b5feba40da5cc6a2d6967bde0c0 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 28 Aug 2013 12:53:09 +0100 Subject: Relax headers type constraints --- src/rabbit_exchange_type_headers.erl | 12 +++++------- src/rabbit_misc.erl | 4 +++- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index baec9c29..c841560e 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -103,17 +103,15 @@ headers_match([{PK, _PT, _PV} | PRest], Data = [{DK, _DT, _DV} | _], headers_match([{PK, PT, PV} | PRest], [{DK, DT, DV} | DRest], AllMatch, AnyMatch, MatchKind) when PK == DK -> {AllMatch1, AnyMatch1} = - if + case rabbit_misc:type_class(PT) == rabbit_misc:type_class(DT) of %% It's not properly specified, but a "no value" in a %% pattern field is supposed to mean simple presence of %% the corresponding data field. I've interpreted that to %% mean a type of "void" for the pattern field. - PT == void -> {AllMatch, true}; - %% Similarly, it's not specified, but I assume that a - %% mismatched type causes a mismatched value. - PT =/= DT -> {false, AnyMatch}; - PV == DV -> {AllMatch, true}; - true -> {false, AnyMatch} + _ when PT == void -> {AllMatch, true}; + false -> {false, AnyMatch}; + _ when PV == DV -> {AllMatch, true}; + _ -> {false, AnyMatch} end, headers_match(PRest, DRest, AllMatch1, AnyMatch1, MatchKind). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index bca9d5ce..8cc83265 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -21,7 +21,8 @@ -export([method_record_type/1, polite_pause/0, polite_pause/1]). -export([die/1, frame_error/2, amqp_error/4, quit/1, protocol_error/3, protocol_error/4, protocol_error/1]). --export([not_found/1, absent/1, assert_args_equivalence/4]). +-export([not_found/1, absent/1]). +-export([type_class/1, assert_args_equivalence/4]). -export([dirty_read/1]). -export([table_lookup/2, set_table_value/4]). -export([r/3, r/2, r_arg/4, rs/1]). @@ -120,6 +121,7 @@ (rabbit_types:amqp_error()) -> channel_or_connection_exit()). -spec(not_found/1 :: (rabbit_types:r(atom())) -> rabbit_types:channel_exit()). -spec(absent/1 :: (rabbit_types:amqqueue()) -> rabbit_types:channel_exit()). +-spec(type_class/1 :: (rabbit_framing:amqp_field_type()) -> atom()). -spec(assert_args_equivalence/4 :: (rabbit_framing:amqp_table(), rabbit_framing:amqp_table(), rabbit_types:r(any()), [binary()]) -> -- cgit v1.2.1 From cf69cc31b7a4b68f86d2693019728c55ab0e875f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Aug 2013 16:25:21 +0100 Subject: Announce consumer priorities. --- src/rabbit_reader.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 9c902703..1a94de8e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -180,7 +180,8 @@ server_capabilities(rabbit_framing_amqp_0_9_1) -> {<<"exchange_exchange_bindings">>, bool, true}, {<<"basic.nack">>, bool, true}, {<<"consumer_cancel_notify">>, bool, true}, - {<<"connection.blocked">>, bool, true}]; + {<<"connection.blocked">>, bool, true}, + {<<"consumer_priorities">>, bool, true}]; server_capabilities(_) -> []. -- cgit v1.2.1 From 477d581198af34c24a875589cf525f9a55f09c31 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Sep 2013 11:16:12 +0100 Subject: add missing type export this somehow got lost in bug 23958 --- src/supervisor2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 5a6dc887..db4c388a 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -85,7 +85,7 @@ %%-------------------------------------------------------------------------- -ifdef(use_specs). --export_type([child_spec/0, startchild_ret/0, strategy/0]). +-export_type([child_spec/0, startchild_ret/0, strategy/0, sup_name/0]). -endif. %%-------------------------------------------------------------------------- -- cgit v1.2.1 From 825c02bdc9a2cad849995e07177b3837ce7f944f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 3 Sep 2013 11:49:13 +0100 Subject: Configure AE by policy. --- src/rabbit_exchange.erl | 9 +++++---- src/rabbit_policies.erl | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 src/rabbit_policies.erl (limited to 'src') diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 49952a4d..bdb0f5db 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -347,11 +347,12 @@ route1(Delivery, Decorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, AlternateDests ++ DecorateDests ++ ExchangeDests)). -process_alternate(#exchange{arguments = []}, _Results) -> %% optimisation - []; -process_alternate(#exchange{name = XName, arguments = Args}, []) -> +process_alternate(X = #exchange{name = XName, arguments = Args}, []) -> case rabbit_misc:r_arg(XName, exchange, Args, <<"alternate-exchange">>) of - undefined -> []; + undefined -> case rabbit_policy:get(<<"alternate-exchange">>, X) of + {ok, AName} -> [rabbit_misc:r(XName, exchange, AName)]; + {error, _} -> [] + end; AName -> [AName] end; process_alternate(_X, _Results) -> diff --git a/src/rabbit_policies.erl b/src/rabbit_policies.erl new file mode 100644 index 00000000..20f461bc --- /dev/null +++ b/src/rabbit_policies.erl @@ -0,0 +1,40 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. +%% + +-module(rabbit_policies). +-behaviour(rabbit_policy_validator). + +-include("rabbit.hrl"). + +-export([register/0, validate_policy/1]). + +-rabbit_boot_step({?MODULE, + [{description, "internal policies"}, + {mfa, {rabbit_policies, register, []}}, + {requires, rabbit_registry}, + {enables, recovery}]}). + +register() -> + [rabbit_registry:register(Class, Name, ?MODULE) || + {Class, Name} <- [{policy_validator, <<"alternate-exchange">>}]], + ok. + +validate_policy([{<<"alternate-exchange">>, Value}]) + when is_binary(Value) -> + ok; +validate_policy([{<<"alternate-exchange">>, Value}]) -> + {error, "~p is not a valid alternate exchange name", [Value]}. + -- cgit v1.2.1 From 5b3ef135f89fb3587f24039fd9498d6538ff6edc Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 3 Sep 2013 12:47:13 +0100 Subject: Reinstate optimisations, plus a bit of abstraction. This entails an API change in the return type of rabbit_policy:get/2. --- src/rabbit_amqqueue.erl | 3 +-- src/rabbit_exchange.erl | 12 +++++------- src/rabbit_policy.erl | 24 +++++++++++++++++------- 3 files changed, 23 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 0673ff8e..31c0268f 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -631,8 +631,7 @@ forget_all_durable(Node) -> internal_delete1(Name)) || #amqqueue{name = Name, pid = Pid} = Q <- Qs, node(Pid) =:= Node, - rabbit_policy:get(<<"ha-mode">>, Q) - =:= {error, not_found}], + rabbit_policy:get(<<"ha-mode">>, Q) =:= undefined], ok end), ok. diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index bdb0f5db..fc131519 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -347,13 +347,11 @@ route1(Delivery, Decorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, AlternateDests ++ DecorateDests ++ ExchangeDests)). -process_alternate(X = #exchange{name = XName, arguments = Args}, []) -> - case rabbit_misc:r_arg(XName, exchange, Args, <<"alternate-exchange">>) of - undefined -> case rabbit_policy:get(<<"alternate-exchange">>, X) of - {ok, AName} -> [rabbit_misc:r(XName, exchange, AName)]; - {error, _} -> [] - end; - AName -> [AName] +process_alternate(X = #exchange{name = XName}, []) -> + case rabbit_policy:get_arg( + <<"alternate-exchange">>, <<"alternate-exchange">>, X) of + undefined -> []; + AName -> [rabbit_misc:r(XName, exchange, AName)] end; process_alternate(_X, _Results) -> []. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 0785d278..d371ac4d 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -26,7 +26,7 @@ -export([register/0]). -export([invalidate/0, recover/0]). --export([name/1, get/2, set/1]). +-export([name/1, get/2, get_arg/3, set/1]). -export([validate/4, notify/4, notify_clear/3]). -export([parse_set/6, set/6, delete/2, lookup/2, list/0, list/1, list_formatted/1, info_keys/0]). @@ -62,15 +62,25 @@ get(Name, #exchange{policy = Policy}) -> get0(Name, Policy); get(Name, EntityName = #resource{virtual_host = VHost}) -> get0(Name, match(EntityName, list(VHost))). -get0(_Name, undefined) -> {error, not_found}; +get0(_Name, undefined) -> undefined; get0(Name, List) -> case pget(definition, List) of - undefined -> {error, not_found}; - Policy -> case pget(Name, Policy) of - undefined -> {error, not_found}; - Value -> {ok, Value} - end + undefined -> undefined; + Policy -> pget(Name, Policy) end. +%% Many heads for optimisation +get_arg(_AName, _PName, #exchange{arguments = [], policy = undefined}) -> + undefined; +get_arg(_AName, PName, X = #exchange{arguments = []}) -> + get(PName, X); +get_arg(AName, _PName, #exchange{arguments = Args, policy = undefined}) -> + rabbit_misc:table_lookup(Args, AName); +get_arg(AName, PName, X = #exchange{arguments = Args}) -> + case rabbit_misc:table_lookup(Args, AName) of + undefined -> get(PName, X); + Arg -> Arg + end. + %%---------------------------------------------------------------------------- %% Gets called during upgrades - therefore must not assume anything about the -- cgit v1.2.1 From 5c0b1272675c8a78b714d2cf17e93a3f34db65d9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 3 Sep 2013 13:13:37 +0100 Subject: Oops --- src/rabbit_policy.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index d371ac4d..3eda44be 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -77,8 +77,8 @@ get_arg(AName, _PName, #exchange{arguments = Args, policy = undefined}) -> rabbit_misc:table_lookup(Args, AName); get_arg(AName, PName, X = #exchange{arguments = Args}) -> case rabbit_misc:table_lookup(Args, AName) of - undefined -> get(PName, X); - Arg -> Arg + undefined -> get(PName, X); + {_Type, Arg} -> Arg end. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 2e0e201e7336fc9381b8e3a29579d5399fdfa42e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 3 Sep 2013 16:12:05 +0100 Subject: Dead lettering by policy. --- src/rabbit_amqqueue.erl | 9 +++++---- src/rabbit_amqqueue_process.erl | 40 ++++++++++++++++++++++++++++------------ src/rabbit_mirror_queue_misc.erl | 2 +- src/rabbit_policies.erl | 17 +++++++++++++++-- 4 files changed, 49 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 31c0268f..e97759cf 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -24,7 +24,7 @@ check_exclusive_access/2, with_exclusive_access_or_die/3, stat/1, deliver/2, deliver_flow/2, requeue/3, ack/3, reject/4]). -export([list/0, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). --export([force_event_refresh/0, wake_up/1]). +-export([force_event_refresh/0, notify_policy_changed/1]). -export([consumers/1, consumers_all/1, consumer_info_keys/0]). -export([basic_get/4, basic_consume/10, basic_cancel/4]). -export([notify_sent/2, notify_sent_queue_down/1, resume/2, flush_all/2]). @@ -111,7 +111,7 @@ -spec(info_all/2 :: (rabbit_types:vhost(), rabbit_types:info_keys()) -> [rabbit_types:infos()]). -spec(force_event_refresh/0 :: () -> 'ok'). --spec(wake_up/1 :: (rabbit_types:amqqueue()) -> 'ok'). +-spec(notify_policy_changed/1 :: (rabbit_types:amqqueue()) -> 'ok'). -spec(consumers/1 :: (rabbit_types:amqqueue()) -> [{pid(), rabbit_types:ctag(), boolean()}]). @@ -298,7 +298,7 @@ policy_changed(Q1, Q2) -> rabbit_mirror_queue_misc:update_mirrors(Q1, Q2), %% Make sure we emit a stats event even if nothing %% mirroring-related has changed - the policy may have changed anyway. - wake_up(Q1). + notify_policy_changed(Q1). start_queue_process(Node, Q) -> {ok, Pid} = rabbit_amqqueue_sup:start_child(Node, [Q]), @@ -500,7 +500,8 @@ force_event_refresh(QNames) -> force_event_refresh(Failed) end. -wake_up(#amqqueue{pid = QPid}) -> gen_server2:cast(QPid, wake_up). +notify_policy_changed(#amqqueue{pid = QPid}) -> + gen_server2:cast(QPid, policy_changed). consumers(#amqqueue{ pid = QPid }) -> delegate:call(QPid, consumers). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 972e6be0..8c6387a3 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -136,7 +136,7 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, rate_timer_ref = RateTRef, senders = Senders, msg_id_to_channel = MTC}, - State2 = process_args(State1), + State2 = process_args_policy(State1), lists:foldl(fun (Delivery, StateN) -> deliver_or_enqueue(Delivery, true, StateN) end, State2, Deliveries). @@ -196,8 +196,9 @@ declare(Recover, From, State = #q{q = Q, BQ = backing_queue_module(Q1), BQS = bq_init(BQ, Q, Recover), recovery_barrier(Recover), - State1 = process_args(State#q{backing_queue = BQ, - backing_queue_state = BQS}), + State1 = process_args_policy( + State#q{backing_queue = BQ, + backing_queue_state = BQS}), rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State1)), rabbit_event:if_enabled(State1, #q.stats_timer, @@ -238,14 +239,23 @@ recovery_barrier(BarrierPid) -> {'DOWN', MRef, process, _, _} -> ok end. -process_args(State = #q{q = #amqqueue{arguments = Arguments}}) -> +process_args_policy(State0 = #q{q = Q = #amqqueue{arguments = Arguments}}) -> + State1 = lists:foldl( + fun({Arg, Fun}, StateN) -> + case rabbit_policy:get(Arg, Q) of + undefined -> StateN; + Val -> Fun(Val, StateN) + end + end, State0, + [{<<"dead-letter-exchange">>, fun init_dlx/2}, + {<<"dead-letter-routing-key">>, fun init_dlx_routing_key/2}]), lists:foldl( - fun({Arg, Fun}, State1) -> + fun({Arg, Fun}, StateN) -> case rabbit_misc:table_lookup(Arguments, Arg) of - {_Type, Val} -> Fun(Val, State1); - undefined -> State1 + {_Type, Val} -> Fun(Val, StateN); + undefined -> StateN end - end, State, + end, State1, [{<<"x-expires">>, fun init_expires/2}, {<<"x-dead-letter-exchange">>, fun init_dlx/2}, {<<"x-dead-letter-routing-key">>, fun init_dlx_routing_key/2}, @@ -959,8 +969,7 @@ i(owner_pid, #q{q = #amqqueue{exclusive_owner = none}}) -> ''; i(owner_pid, #q{q = #amqqueue{exclusive_owner = ExclusiveOwner}}) -> ExclusiveOwner; -i(policy, #q{q = #amqqueue{name = Name}}) -> - {ok, Q} = rabbit_amqqueue:lookup(Name), +i(policy, #q{q = Q}) -> case rabbit_policy:name(Q) of none -> ''; Policy -> Policy @@ -1388,8 +1397,15 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, end end); -handle_cast(wake_up, State) -> - noreply(State). +handle_cast(policy_changed, State = #q{q = #amqqueue{name = Name}}) -> + %% We depend on the #q.q field being up to date at least WRT + %% policy (but not slave pids) in various places, so when it + %% changes we go and read it from Mnesia again. + %% + %% This also has the side effect of waking us up so we emit a + %% stats event - so event consumers see the changed policy. + {ok, Q} = rabbit_amqqueue:lookup(Name), + noreply(process_args_policy(State#q{q = Q})). handle_info(maybe_expire, State) -> case is_unused(State) of diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index eded0411..2d55cad4 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -221,7 +221,7 @@ store_updated_slaves(Q = #amqqueue{slave_pids = SPids, Q1 = Q#amqqueue{sync_slave_pids = SSPids1}, ok = rabbit_amqqueue:store_queue(Q1), %% Wake it up so that we emit a stats event - rabbit_amqqueue:wake_up(Q1), + rabbit_amqqueue:notify_policy_changed(Q1), Q1. %%---------------------------------------------------------------------------- diff --git a/src/rabbit_policies.erl b/src/rabbit_policies.erl index 20f461bc..a77e1e7b 100644 --- a/src/rabbit_policies.erl +++ b/src/rabbit_policies.erl @@ -29,12 +29,25 @@ register() -> [rabbit_registry:register(Class, Name, ?MODULE) || - {Class, Name} <- [{policy_validator, <<"alternate-exchange">>}]], + {Class, Name} <- [{policy_validator, <<"alternate-exchange">>}, + {policy_validator, <<"dead-letter-exchange">>}, + {policy_validator, <<"dead-letter-routing-key">>}]], ok. validate_policy([{<<"alternate-exchange">>, Value}]) when is_binary(Value) -> ok; validate_policy([{<<"alternate-exchange">>, Value}]) -> - {error, "~p is not a valid alternate exchange name", [Value]}. + {error, "~p is not a valid alternate exchange name", [Value]}; +validate_policy([{<<"dead-letter-exchange">>, Value}]) + when is_binary(Value) -> + ok; +validate_policy([{<<"dead-letter-exchange">>, Value}]) -> + {error, "~p is not a valid dead letter exchange name", [Value]}; + +validate_policy([{<<"dead-letter-routing-key">>, Value}]) + when is_binary(Value) -> + ok; +validate_policy([{<<"dead-letter-routing-key">>, Value}]) -> + {error, "~p is not a valid dead letter routing key", [Value]}. -- cgit v1.2.1 From 3af53e2a845f8d6329a807947502f6909db391eb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 3 Sep 2013 16:45:19 +0100 Subject: Oops --- src/rabbit_policies.erl | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/rabbit_policies.erl b/src/rabbit_policies.erl index a77e1e7b..ce763777 100644 --- a/src/rabbit_policies.erl +++ b/src/rabbit_policies.erl @@ -34,20 +34,25 @@ register() -> {policy_validator, <<"dead-letter-routing-key">>}]], ok. -validate_policy([{<<"alternate-exchange">>, Value}]) +validate_policy(Terms) -> + lists:foldl(fun ({Key, Value}, ok) -> validate_policy0(Key, Value); + (_, Error) -> Error + end, ok, Terms). + +validate_policy0(<<"alternate-exchange">>, Value) when is_binary(Value) -> ok; -validate_policy([{<<"alternate-exchange">>, Value}]) -> +validate_policy0(<<"alternate-exchange">>, Value) -> {error, "~p is not a valid alternate exchange name", [Value]}; -validate_policy([{<<"dead-letter-exchange">>, Value}]) +validate_policy0(<<"dead-letter-exchange">>, Value) when is_binary(Value) -> ok; -validate_policy([{<<"dead-letter-exchange">>, Value}]) -> +validate_policy0(<<"dead-letter-exchange">>, Value) -> {error, "~p is not a valid dead letter exchange name", [Value]}; -validate_policy([{<<"dead-letter-routing-key">>, Value}]) +validate_policy0(<<"dead-letter-routing-key">>, Value) when is_binary(Value) -> ok; -validate_policy([{<<"dead-letter-routing-key">>, Value}]) -> +validate_policy0(<<"dead-letter-routing-key">>, Value) -> {error, "~p is not a valid dead letter routing key", [Value]}. -- cgit v1.2.1 From 720cc5eaa993bcb0c1954ed8380949db1c6f3ea2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 4 Sep 2013 16:02:09 +0100 Subject: Don't page when the disk alarm has gone off. --- src/rabbit_memory_monitor.erl | 116 +++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index b8d8023e..2e9a820c 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -25,7 +25,7 @@ -behaviour(gen_server2). -export([start_link/0, register/2, deregister/1, - report_ram_duration/2, stop/0]). + report_ram_duration/2, stop/0, conserve_resources/3]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -36,7 +36,8 @@ queue_durations, %% ets #process queue_duration_sum, %% sum of all queue_durations queue_duration_count, %% number of elements in sum - desired_duration %% the desired queue duration + desired_duration, %% the desired queue duration + disk_alarm %% disable paging, disk alarm has fired }). -define(SERVER, ?MODULE). @@ -86,6 +87,11 @@ report_ram_duration(Pid, QueueDuration) -> stop() -> gen_server2:cast(?SERVER, stop). +conserve_resources(Pid, disk, Conserve) -> + gen_server2:cast(Pid, {disk_alarm, Conserve}); +conserve_resources(_Pid, _Source, _Conserve) -> + ok. + %%---------------------------------------------------------------------------- %% Gen_server callbacks %%---------------------------------------------------------------------------- @@ -94,13 +100,14 @@ init([]) -> {ok, TRef} = timer:send_interval(?DEFAULT_UPDATE_INTERVAL, update), Ets = ets:new(?TABLE_NAME, [set, private, {keypos, #process.pid}]), - + Alarms = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}), {ok, internal_update( #state { timer = TRef, queue_durations = Ets, queue_duration_sum = 0.0, queue_duration_count = 0, - desired_duration = infinity })}. + desired_duration = infinity, + disk_alarm = lists:member(disk, Alarms)})}. handle_call({report_ram_duration, Pid, QueueDuration}, From, State = #state { queue_duration_sum = Sum, @@ -137,6 +144,12 @@ handle_call({register, Pid, MFA}, _From, handle_call(_Request, _From, State) -> {noreply, State}. +handle_cast({disk_alarm, Alarm}, State = #state{disk_alarm = Alarm}) -> + {noreply, State}; + +handle_cast({disk_alarm, Alarm}, State) -> + {noreply, internal_update(State#state{disk_alarm = Alarm})}; + handle_cast({deregister, Pid}, State) -> {noreply, internal_deregister(Pid, true, State)}; @@ -192,10 +205,18 @@ internal_deregister(Pid, Demonitor, queue_duration_count = Count1 } end. -internal_update(State = #state { queue_durations = Durations, - desired_duration = DesiredDurationAvg, - queue_duration_sum = Sum, - queue_duration_count = Count }) -> +internal_update(State = #state{queue_durations = Durations, + desired_duration = DesiredDurationAvg, + disk_alarm = DiskAlarm}) -> + DesiredDurationAvg1 = desired_duration_average(State), + State1 = State#state{desired_duration = DesiredDurationAvg1}, + maybe_inform_queues( + DiskAlarm, DesiredDurationAvg, DesiredDurationAvg1, Durations), + State1. + +desired_duration_average(#state{queue_duration_sum = Sum, + queue_duration_count = Count, + disk_alarm = DiskAlarm}) -> {ok, LimitThreshold} = application:get_env(rabbit, vm_memory_high_watermark_paging_ratio), MemoryLimit = vm_memory_monitor:get_memory_limit(), @@ -203,46 +224,61 @@ internal_update(State = #state { queue_durations = Durations, true -> erlang:memory(total) / MemoryLimit; false -> infinity end, - DesiredDurationAvg1 = - if MemoryRatio =:= infinity -> - 0.0; - MemoryRatio < LimitThreshold orelse Count == 0 -> - infinity; - MemoryRatio < ?SUM_INC_THRESHOLD -> - ((Sum + ?SUM_INC_AMOUNT) / Count) / MemoryRatio; - true -> - (Sum / Count) / MemoryRatio - end, - State1 = State #state { desired_duration = DesiredDurationAvg1 }, + if DiskAlarm -> + infinity; + MemoryRatio =:= infinity -> + 0.0; + MemoryRatio < LimitThreshold orelse Count == 0 -> + infinity; + MemoryRatio < ?SUM_INC_THRESHOLD -> + ((Sum + ?SUM_INC_AMOUNT) / Count) / MemoryRatio; + true -> + (Sum / Count) / MemoryRatio + end. +maybe_inform_queues(false, DesiredDurationAvg, DesiredDurationAvg1, + Durations) -> %% only inform queues immediately if the desired duration has %% decreased case DesiredDurationAvg1 == infinity orelse (DesiredDurationAvg /= infinity andalso DesiredDurationAvg1 >= DesiredDurationAvg) of - true -> - ok; - false -> - true = - ets:foldl( - fun (Proc = #process { reported = QueueDuration, - sent = PrevSendDuration, - callback = {M, F, A} }, true) -> - case should_send(QueueDuration, PrevSendDuration, - DesiredDurationAvg1) of - true -> ok = erlang:apply( - M, F, A ++ [DesiredDurationAvg1]), - ets:insert( - Durations, - Proc #process { - sent = DesiredDurationAvg1}); - false -> true - end - end, true, Durations) - end, - State1. + true -> ok; + false -> inform_queues(DesiredDurationAvg1, Durations, + fun should_send/3) + end; +maybe_inform_queues(true, DesiredDurationAvg, DesiredDurationAvg1, + Durations) -> + case DesiredDurationAvg1 == infinity andalso + DesiredDurationAvg /= infinity of + true -> inform_queues(DesiredDurationAvg1, Durations, + fun should_send_disk_alarm/3); + false -> ok + end. + +inform_queues(DesiredDurationAvg1, Durations, If) -> + true = + ets:foldl( + fun (Proc = #process{reported = QueueDuration, + sent = PrevSendDuration, + callback = {M, F, A}}, true) -> + case If(QueueDuration, PrevSendDuration, + DesiredDurationAvg1) of + true -> ok = erlang:apply( + M, F, A ++ [DesiredDurationAvg1]), + ets:insert( + Durations, + Proc#process{sent = DesiredDurationAvg1}); + false -> true + end + end, true, Durations). + should_send(infinity, infinity, _) -> true; should_send(infinity, D, DD) -> DD < D; should_send(D, infinity, DD) -> DD < D; should_send(D1, D2, DD) -> DD < lists:min([D1, D2]). + +should_send_disk_alarm(_, infinity, _) -> false; +should_send_disk_alarm(_, _, infinity) -> true; +should_send_disk_alarm(_, _, _) -> false. -- cgit v1.2.1 From b420b911870e74cee1163f565a105d89337ae702 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 6 Sep 2013 16:37:20 +0100 Subject: Hook TTL / max length / expiry into policy system. --- src/rabbit_amqqueue_process.erl | 61 ++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f5c6cf85..9c5b5fa2 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -263,40 +263,45 @@ recovery_barrier(BarrierPid) -> {'DOWN', MRef, process, _, _} -> ok end. -process_args_policy(State0 = #q{q = Q = #amqqueue{arguments = Arguments}}) -> - State1 = lists:foldl( - fun({Arg, Fun}, StateN) -> - case rabbit_policy:get(Arg, Q) of - undefined -> StateN; - Val -> Fun(Val, StateN) - end - end, State0, - [{<<"dead-letter-exchange">>, fun init_dlx/2}, - {<<"dead-letter-routing-key">>, fun init_dlx_routing_key/2}]), +process_args_policy(State = #q{q = Q}) -> lists:foldl( - fun({Arg, Fun}, StateN) -> - case rabbit_misc:table_lookup(Arguments, Arg) of - {_Type, Val} -> Fun(Val, StateN); - undefined -> StateN - end - end, State1, - [{<<"x-expires">>, fun init_expires/2}, - {<<"x-dead-letter-exchange">>, fun init_dlx/2}, - {<<"x-dead-letter-routing-key">>, fun init_dlx_routing_key/2}, - {<<"x-message-ttl">>, fun init_ttl/2}, - {<<"x-max-length">>, fun init_max_length/2}]). - -init_expires(Expires, State) -> ensure_expiry_timer(State#q{expires = Expires}). - -init_ttl(TTL, State) -> drop_expired_msgs(State#q{ttl = TTL}). + fun({Name, Resolve, Fun}, StateN) -> + Fun(args_policy_lookup(Name, Resolve, Q), StateN) + end, State, + [{<<"expires">>, fun res_min/2, fun init_exp/2}, + {<<"dead-letter-exchange">>, fun res_arg/2, fun init_dlx/2}, + {<<"dead-letter-routing-key">>, fun res_arg/2, fun init_dlx_rkey/2}, + {<<"message-ttl">>, fun res_min/2, fun init_ttl/2}, + {<<"max-length">>, fun res_min/2, fun init_max_length/2}]). + +args_policy_lookup(Name, Resolve, Q = #amqqueue{arguments = Args}) -> + AName = <<"x-", Name/binary>>, + case {rabbit_policy:get(Name, Q), rabbit_misc:table_lookup(Args, AName)} of + {undefined, undefined} -> undefined; + {undefined, {_Type, Val}} -> Val; + {Val, undefined} -> Val; + {PolVal, {_Type, ArgVal}} -> Resolve(PolVal, ArgVal) + end. + +res_arg(_PolVal, ArgVal) -> ArgVal. +res_min(PolVal, ArgVal) -> erlang:min(PolVal, ArgVal). + +init_exp(undefined, State) -> stop_expiry_timer(State#q{expires = undefined}); +init_exp(Expires, State) -> ensure_expiry_timer(State#q{expires = Expires}). +init_ttl(undefined, State) -> stop_ttl_timer(State#q{ttl = undefined}); +init_ttl(TTL, State) -> drop_expired_msgs(State#q{ttl = TTL}). + +init_dlx(undefined, State) -> + State#q{dlx = undefined}; init_dlx(DLX, State = #q{q = #amqqueue{name = QName}}) -> State#q{dlx = rabbit_misc:r(QName, exchange, DLX)}. -init_dlx_routing_key(RoutingKey, State) -> - State#q{dlx_routing_key = RoutingKey}. +init_dlx_rkey(RoutingKey, State) -> State#q{dlx_routing_key = RoutingKey}. -init_max_length(MaxLen, State) -> State#q{max_length = MaxLen}. +init_max_length(MaxLen, State) -> + {_Dropped, State1} = maybe_drop_head(State#q{max_length = MaxLen}), + State1. terminate_shutdown(Fun, State) -> State1 = #q{backing_queue_state = BQS} = -- cgit v1.2.1 From 75bd64e77ba5aca38859971156404faa49409972 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Sep 2013 12:27:30 +0100 Subject: Explain a little. --- src/rabbit_amqqueue_process.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ea0a3153..9f864d28 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1117,7 +1117,7 @@ handle_call({init, Recover}, From, new -> rabbit_log:warning( "exclusive owner for ~s went away~n", [rabbit_misc:rs(QName)]); - _ -> ok + _ -> ok %% [1] end, BQ = backing_queue_module(Q), BQS = bq_init(BQ, Q, Recover), @@ -1126,6 +1126,10 @@ handle_call({init, Recover}, From, State#q{backing_queue = BQ, backing_queue_state = BQS}} end; +%% [1] You used to be able to declare an exclusive durable queue. Sadly we +%% need to still tidy up after that case, there could be the remnants of one +%% left over from an upgrade. + handle_call(info, _From, State) -> reply(infos(?INFO_KEYS, State), State); -- cgit v1.2.1 From 31ef2e6cac1457aa0221d0bf8d0829ae3cfe64ba Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Sep 2013 17:06:40 +0100 Subject: Allow use of the new policies. --- src/rabbit_misc.erl | 3 --- src/rabbit_policies.erl | 27 +++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 8cc83265..00c4eaf3 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -76,9 +76,6 @@ R =:= noproc; R =:= noconnection; R =:= nodedown; R =:= normal; R =:= shutdown). -%% This is dictated by `erlang:send_after' on which we depend to implement TTL. --define(MAX_EXPIRY_TIMER, 4294967295). - %%---------------------------------------------------------------------------- -ifdef(use_specs). diff --git a/src/rabbit_policies.erl b/src/rabbit_policies.erl index ce763777..fec83eef 100644 --- a/src/rabbit_policies.erl +++ b/src/rabbit_policies.erl @@ -31,7 +31,10 @@ register() -> [rabbit_registry:register(Class, Name, ?MODULE) || {Class, Name} <- [{policy_validator, <<"alternate-exchange">>}, {policy_validator, <<"dead-letter-exchange">>}, - {policy_validator, <<"dead-letter-routing-key">>}]], + {policy_validator, <<"dead-letter-routing-key">>}, + {policy_validator, <<"message-ttl">>}, + {policy_validator, <<"expires">>}, + {policy_validator, <<"max-length">>}]], ok. validate_policy(Terms) -> @@ -55,4 +58,24 @@ validate_policy0(<<"dead-letter-routing-key">>, Value) when is_binary(Value) -> ok; validate_policy0(<<"dead-letter-routing-key">>, Value) -> - {error, "~p is not a valid dead letter routing key", [Value]}. + {error, "~p is not a valid dead letter routing key", [Value]}; + +validate_policy0(<<"message-ttl">>, Value) + when is_integer(Value), Value >= 0, Value =< ?MAX_EXPIRY_TIMER -> + ok; +validate_policy0(<<"message-ttl">>, Value) -> + {error, "~p is not a valid message TTL", [Value]}; + +validate_policy0(<<"expires">>, Value) + when is_integer(Value), Value >= 0, Value =< ?MAX_EXPIRY_TIMER -> + ok; +validate_policy0(<<"expires">>, Value) -> + {error, "~p is not a valid queue expiry", [Value]}; + +validate_policy0(<<"max-length">>, Value) + when is_integer(Value), Value >= 0 -> + ok; +validate_policy0(<<"max-length">>, Value) -> + {error, "~p is not a valid maximum length", [Value]}. + + -- cgit v1.2.1 From 9b1caeb7c72008ae2ce435a20b7960d053039eb0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Sep 2013 17:23:11 +0100 Subject: Expires cannot be 0. --- src/rabbit_policies.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_policies.erl b/src/rabbit_policies.erl index fec83eef..c4a37e7a 100644 --- a/src/rabbit_policies.erl +++ b/src/rabbit_policies.erl @@ -67,7 +67,7 @@ validate_policy0(<<"message-ttl">>, Value) -> {error, "~p is not a valid message TTL", [Value]}; validate_policy0(<<"expires">>, Value) - when is_integer(Value), Value >= 0, Value =< ?MAX_EXPIRY_TIMER -> + when is_integer(Value), Value >= 1, Value =< ?MAX_EXPIRY_TIMER -> ok; validate_policy0(<<"expires">>, Value) -> {error, "~p is not a valid queue expiry", [Value]}; -- cgit v1.2.1 From ac52bbd8546648702bfc8ce43245105da0f5635b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 11 Sep 2013 17:14:25 +0100 Subject: Version TTL and expiry messages, and ignore the wrong ones. Also stop timers and then start them again when re-initing. --- src/rabbit_amqqueue_process.erl | 42 ++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9c5b5fa2..c7d345d2 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -52,6 +52,7 @@ dlx, dlx_routing_key, max_length, + args_policy_version, status }). @@ -150,7 +151,8 @@ init_state(Q) -> active_consumers = priority_queue:new(), senders = pmon:new(delegate), msg_id_to_channel = gb_trees:empty(), - status = running}, + status = running, + args_policy_version = 0}, rabbit_event:init_stats_timer(State, #q.stats_timer). terminate(shutdown = R, State = #q{backing_queue = BQ}) -> @@ -263,11 +265,12 @@ recovery_barrier(BarrierPid) -> {'DOWN', MRef, process, _, _} -> ok end. -process_args_policy(State = #q{q = Q}) -> +process_args_policy(State = #q{q = Q, + args_policy_version = N}) -> lists:foldl( fun({Name, Resolve, Fun}, StateN) -> Fun(args_policy_lookup(Name, Resolve, Q), StateN) - end, State, + end, State#q{args_policy_version = N + 1}, [{<<"expires">>, fun res_min/2, fun init_exp/2}, {<<"dead-letter-exchange">>, fun res_arg/2, fun init_dlx/2}, {<<"dead-letter-routing-key">>, fun res_arg/2, fun init_dlx_rkey/2}, @@ -286,11 +289,16 @@ args_policy_lookup(Name, Resolve, Q = #amqqueue{arguments = Args}) -> res_arg(_PolVal, ArgVal) -> ArgVal. res_min(PolVal, ArgVal) -> erlang:min(PolVal, ArgVal). +%% In both these we init with the undefined variant first to stop any +%% existing timer, then start a new one which may fire after a +%% different time. init_exp(undefined, State) -> stop_expiry_timer(State#q{expires = undefined}); -init_exp(Expires, State) -> ensure_expiry_timer(State#q{expires = Expires}). +init_exp(Expires, State) -> State1 = init_exp(undefined, State), + ensure_expiry_timer(State1#q{expires = Expires}). init_ttl(undefined, State) -> stop_ttl_timer(State#q{ttl = undefined}); -init_ttl(TTL, State) -> drop_expired_msgs(State#q{ttl = TTL}). +init_ttl(TTL, State) -> State1 = init_ttl(undefined, State), + drop_expired_msgs(State1#q{ttl = TTL}). init_dlx(undefined, State) -> State#q{dlx = undefined}; @@ -363,11 +371,12 @@ stop_rate_timer(State) -> rabbit_misc:stop_timer(State, #q.rate_timer_ref). %% configured period. ensure_expiry_timer(State = #q{expires = undefined}) -> State; -ensure_expiry_timer(State = #q{expires = Expires}) -> +ensure_expiry_timer(State = #q{expires = Expires, + args_policy_version = Version}) -> case is_unused(State) of true -> NewState = stop_expiry_timer(State), rabbit_misc:ensure_timer(NewState, #q.expiry_timer_ref, - Expires, maybe_expire); + Expires, {maybe_expire, Version}); false -> State end. @@ -375,12 +384,13 @@ stop_expiry_timer(State) -> rabbit_misc:stop_timer(State, #q.expiry_timer_ref). ensure_ttl_timer(undefined, State) -> State; -ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> +ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined, + args_policy_version = Version}) -> After = (case Expiry - now_micros() of V when V > 0 -> V + 999; %% always fire later _ -> 0 end) div 1000, - TRef = erlang:send_after(After, self(), drop_expired), + TRef = erlang:send_after(After, self(), {drop_expired, Version}), State#q{ttl_timer_ref = TRef, ttl_timer_expiry = Expiry}; ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = TRef, ttl_timer_expiry = TExpiry}) @@ -1107,8 +1117,8 @@ prioritise_info(Msg, _Len, #q{q = #amqqueue{exclusive_owner = DownPid}}) -> case Msg of {'DOWN', _, process, DownPid, _} -> 8; update_ram_duration -> 8; - maybe_expire -> 8; - drop_expired -> 8; + {maybe_expire, _Version} -> 8; + {drop_expired, _Version} -> 8; emit_stats -> 7; sync_timeout -> 6; _ -> 0 @@ -1451,17 +1461,23 @@ handle_cast(policy_changed, State = #q{q = #amqqueue{name = Name}}) -> {ok, Q} = rabbit_amqqueue:lookup(Name), noreply(process_args_policy(State#q{q = Q})). -handle_info(maybe_expire, State) -> +handle_info({maybe_expire, Vsn}, State = #q{args_policy_version = Vsn}) -> case is_unused(State) of true -> stop(State); false -> noreply(State#q{expiry_timer_ref = undefined}) end; -handle_info(drop_expired, State) -> +handle_info({maybe_expire, _Vsn}, State) -> + noreply(State); + +handle_info({drop_expired, Vsn}, State = #q{args_policy_version = Vsn}) -> WasEmpty = is_empty(State), State1 = drop_expired_msgs(State#q{ttl_timer_ref = undefined}), noreply(maybe_send_drained(WasEmpty, State1)); +handle_info({drop_expired, _Vsn}, State) -> + noreply(State); + handle_info(emit_stats, State) -> emit_stats(State), %% Don't call noreply/1, we don't want to set timers -- cgit v1.2.1 From 0a6653b14a1c47868ecaf695f919f35841d990f5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 11 Sep 2013 17:45:15 +0100 Subject: That "optimisation" is actually buggy since table_lookup/2 returns a 2-tuple on success. And by the time you correct for that you realise it's not even an optimisation any more. --- src/rabbit_policy.erl | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index ed14e649..de10b30f 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -74,8 +74,6 @@ get_arg(_AName, _PName, #exchange{arguments = [], policy = undefined}) -> undefined; get_arg(_AName, PName, X = #exchange{arguments = []}) -> get(PName, X); -get_arg(AName, _PName, #exchange{arguments = Args, policy = undefined}) -> - rabbit_misc:table_lookup(Args, AName); get_arg(AName, PName, X = #exchange{arguments = Args}) -> case rabbit_misc:table_lookup(Args, AName) of undefined -> get(PName, X); -- cgit v1.2.1 From ab795cb8dfa6948e6c3a3de82389e4667fad8218 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 13 Sep 2013 14:34:05 +0100 Subject: FAST_RATE based dynamic disk check interval. --- src/rabbit_disk_monitor.erl | 77 ++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 5aaa1b2d..f153641e 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -23,16 +23,22 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([get_disk_free_limit/0, set_disk_free_limit/1, get_check_interval/0, - set_check_interval/1, get_disk_free/0]). +-export([get_disk_free_limit/0, set_disk_free_limit/1, + get_min_check_interval/0, set_min_check_interval/1, + get_max_check_interval/0, set_max_check_interval/1, + get_disk_free/0]). -define(SERVER, ?MODULE). --define(DEFAULT_DISK_CHECK_INTERVAL, 10000). +-define(DEFAULT_MIN_DISK_CHECK_INTERVAL, 100). +-define(DEFAULT_MAX_DISK_CHECK_INTERVAL, 10000). +%% 250MB/s i.e. 250kB/ms +-define(FAST_RATE, (250 * 1000)). -record(state, {dir, limit, actual, - timeout, + min_interval, + max_interval, timer, alarmed }). @@ -45,8 +51,10 @@ -spec(start_link/1 :: (disk_free_limit()) -> rabbit_types:ok_pid_or_error()). -spec(get_disk_free_limit/0 :: () -> integer()). -spec(set_disk_free_limit/1 :: (disk_free_limit()) -> 'ok'). --spec(get_check_interval/0 :: () -> integer()). --spec(set_check_interval/1 :: (integer()) -> 'ok'). +-spec(get_min_check_interval/0 :: () -> integer()). +-spec(set_min_check_interval/1 :: (integer()) -> 'ok'). +-spec(get_max_check_interval/0 :: () -> integer()). +-spec(set_max_check_interval/1 :: (integer()) -> 'ok'). -spec(get_disk_free/0 :: () -> (integer() | 'unknown')). -endif. @@ -61,11 +69,17 @@ get_disk_free_limit() -> set_disk_free_limit(Limit) -> gen_server:call(?MODULE, {set_disk_free_limit, Limit}, infinity). -get_check_interval() -> - gen_server:call(?MODULE, get_check_interval, infinity). +get_min_check_interval() -> + gen_server:call(?MODULE, get_min_check_interval, infinity). -set_check_interval(Interval) -> - gen_server:call(?MODULE, {set_check_interval, Interval}, infinity). +set_min_check_interval(Interval) -> + gen_server:call(?MODULE, {set_min_check_interval, Interval}, infinity). + +get_max_check_interval() -> + gen_server:call(?MODULE, get_max_check_interval, infinity). + +set_max_check_interval(Interval) -> + gen_server:call(?MODULE, {set_max_check_interval, Interval}, infinity). get_disk_free() -> gen_server:call(?MODULE, get_disk_free, infinity). @@ -78,16 +92,15 @@ start_link(Args) -> gen_server:start_link({local, ?SERVER}, ?MODULE, [Args], []). init([Limit]) -> - TRef = start_timer(?DEFAULT_DISK_CHECK_INTERVAL), Dir = dir(), - State = #state { dir = Dir, - timeout = ?DEFAULT_DISK_CHECK_INTERVAL, - timer = TRef, - alarmed = false}, + State = #state{dir = Dir, + min_interval = ?DEFAULT_MIN_DISK_CHECK_INTERVAL, + max_interval = ?DEFAULT_MAX_DISK_CHECK_INTERVAL, + alarmed = false}, case {catch get_disk_free(Dir), vm_memory_monitor:get_total_memory()} of {N1, N2} when is_integer(N1), is_integer(N2) -> - {ok, set_disk_limits(State, Limit)}; + {ok, start_timer(set_disk_limits(State, Limit))}; Err -> rabbit_log:info("Disabling disk free space monitoring " "on unsupported platform: ~p~n", [Err]), @@ -100,12 +113,17 @@ handle_call(get_disk_free_limit, _From, State) -> handle_call({set_disk_free_limit, Limit}, _From, State) -> {reply, ok, set_disk_limits(State, Limit)}; -handle_call(get_check_interval, _From, State) -> - {reply, State#state.timeout, State}; +handle_call(get_min_check_interval, _From, State) -> + {reply, State#state.min_interval, State}; + +handle_call(get_max_check_interval, _From, State) -> + {reply, State#state.max_interval, State}; + +handle_call({set_min_check_interval, MinInterval}, _From, State) -> + {reply, ok, State#state{min_interval = MinInterval}}; -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({set_max_check_interval, MaxInterval}, _From, State) -> + {reply, ok, State#state{max_interval = MaxInterval}}; handle_call(get_disk_free, _From, State = #state { actual = Actual }) -> {reply, Actual, State}; @@ -117,7 +135,7 @@ handle_cast(_Request, State) -> {noreply, State}. handle_info(update, State) -> - {noreply, internal_update(State)}; + {noreply, start_timer(internal_update(State))}; handle_info(_Info, State) -> {noreply, State}. @@ -193,6 +211,15 @@ emit_update_info(StateStr, CurrentFree, Limit) -> "Disk free space ~s. Free bytes:~p Limit:~p~n", [StateStr, CurrentFree, Limit]). -start_timer(Timeout) -> - {ok, TRef} = timer:send_interval(Timeout, update), - TRef. +start_timer(State) -> + State#state{timer = erlang:send_after(interval(State), self(), update)}. + +interval(#state{alarmed = true, + max_interval = MaxInterval}) -> + MaxInterval; +interval(#state{limit = Limit, + actual = Actual, + min_interval = MinInterval, + max_interval = MaxInterval}) -> + IdealInterval = 2 * (Actual - Limit) / ?FAST_RATE, + trunc(erlang:max(MinInterval, erlang:min(MaxInterval, IdealInterval))). -- cgit v1.2.1 From 45f1fd2e31667967dd38cf84e32f5c5ace030dfa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 13 Sep 2013 14:50:06 +0100 Subject: Oops, we completely broke HA there. --- src/rabbit_mirror_queue_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 2d55cad4..4dd19bd3 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -257,8 +257,8 @@ policy(Policy, Q) -> module(#amqqueue{} = Q) -> case rabbit_policy:get(<<"ha-mode">>, Q) of - {ok, Mode} -> module(Mode); - _ -> not_mirrored + undefined -> not_mirrored; + Mode -> module(Mode) end; module(Mode) when is_binary(Mode) -> -- cgit v1.2.1 From 6bb0df6a791df386f217f6dc9d6feccda55f239a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 13 Sep 2013 16:43:03 +0100 Subject: ...and again. --- src/rabbit_mirror_queue_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 4dd19bd3..2a257f91 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -251,8 +251,8 @@ suggested_queue_nodes(Q, All) -> policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of - {ok, P} -> P; - _ -> none + undefined -> none; + P -> P end. module(#amqqueue{} = Q) -> -- cgit v1.2.1 From 10cdb2aff04b13b33f307bce683446f3a41543f2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 13 Sep 2013 16:55:23 +0100 Subject: Dialyser points out that this branch can never happen; maybe_forget_sender/3 is always invoked with down_from_* as ChState. --- src/rabbit_mirror_queue_slave.erl | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index d508f0e9..56a397b4 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -652,7 +652,6 @@ confirm_sender_death(Pid) -> [self(), rabbit_mirror_queue_master, Fun]), ok. -forget_sender(running, _) -> false; forget_sender(_, running) -> false; forget_sender(Down1, Down2) when Down1 =/= Down2 -> true. -- cgit v1.2.1 From f238ba9a2c0cbcd12c148cfe2df82fa794160a38 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 23 Sep 2013 16:34:47 +0100 Subject: Slightly better comments. --- src/rabbit_memory_monitor.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index 2e9a820c..4f8202cd 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -236,10 +236,10 @@ desired_duration_average(#state{queue_duration_sum = Sum, (Sum / Count) / MemoryRatio end. +%% In normal use, we only inform queues immediately if the desired +%% duration has decreased, we want to ensure timely paging. maybe_inform_queues(false, DesiredDurationAvg, DesiredDurationAvg1, Durations) -> - %% only inform queues immediately if the desired duration has - %% decreased case DesiredDurationAvg1 == infinity orelse (DesiredDurationAvg /= infinity andalso DesiredDurationAvg1 >= DesiredDurationAvg) of @@ -247,6 +247,9 @@ maybe_inform_queues(false, DesiredDurationAvg, DesiredDurationAvg1, false -> inform_queues(DesiredDurationAvg1, Durations, fun should_send/3) end; +%% When the disk alarm has gone off though, we want to inform queues +%% immediately if the desired duration has *increased* - we want to +%% ensure timely stopping paging. maybe_inform_queues(true, DesiredDurationAvg, DesiredDurationAvg1, Durations) -> case DesiredDurationAvg1 == infinity andalso -- cgit v1.2.1 From bdc0e6ec4e13abb2781eebd2273fdc26eaf9242f Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 24 Sep 2013 12:05:37 +0100 Subject: Report authentication failures by sending connection.close --- src/rabbit_direct.erl | 12 +++++++----- src/rabbit_reader.erl | 29 ++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index a7ee3276..1577b0d5 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -37,8 +37,8 @@ rabbit_event:event_props()) -> rabbit_types:ok_or_error2( {rabbit_types:user(), rabbit_framing:amqp_table()}, - 'broker_not_found_on_node' | 'auth_failure' | - 'access_refused')). + 'broker_not_found_on_node' | + {'auth_failure', string()} | 'access_refused')). -spec(start_channel/9 :: (rabbit_channel:channel_number(), pid(), pid(), string(), rabbit_types:protocol(), rabbit_types:user(), rabbit_types:vhost(), @@ -90,9 +90,11 @@ connect(Username, VHost, Protocol, Pid, Infos) -> connect0(AuthFun, VHost, Protocol, Pid, Infos) -> case rabbit:is_running() of true -> case AuthFun() of - {ok, User} -> connect(User, VHost, Protocol, Pid, - Infos); - {refused, _M, _A} -> {error, auth_failure} + {ok, User} -> + connect(User, VHost, Protocol, Pid, Infos); + {refused, Msg, Args} -> + Reason = io_lib:format(Msg, Args), + {error, {auth_failure, Reason}} end; false -> {error, broker_not_found_on_node} end. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 1a94de8e..bc82318a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -176,12 +176,13 @@ server_properties(Protocol) -> NormalizedConfigServerProps). server_capabilities(rabbit_framing_amqp_0_9_1) -> - [{<<"publisher_confirms">>, bool, true}, - {<<"exchange_exchange_bindings">>, bool, true}, - {<<"basic.nack">>, bool, true}, - {<<"consumer_cancel_notify">>, bool, true}, - {<<"connection.blocked">>, bool, true}, - {<<"consumer_priorities">>, bool, true}]; + [{<<"publisher_confirms">>, bool, true}, + {<<"exchange_exchange_bindings">>, bool, true}, + {<<"basic.nack">>, bool, true}, + {<<"consumer_cancel_notify">>, bool, true}, + {<<"connection.blocked">>, bool, true}, + {<<"consumer_priorities">>, bool, true}, + {<<"authentication_failure_close">>, bool, true}]; server_capabilities(_) -> []. @@ -965,14 +966,24 @@ auth_mechanisms_binary(Sock) -> auth_phase(Response, State = #v1{connection = Connection = #connection{protocol = Protocol, + capabilities = Capabilities, auth_mechanism = {Name, AuthMechanism}, auth_state = AuthState}, sock = Sock}) -> case AuthMechanism:handle_response(Response, AuthState) of {refused, Msg, Args} -> - rabbit_misc:protocol_error( - access_refused, "~s login refused: ~s", - [Name, io_lib:format(Msg, Args)]); + AmqpError = rabbit_misc:amqp_error( + access_refused, "~s login refused: ~s", + [Name, io_lib:format(Msg, Args)], none), + case rabbit_misc:table_lookup(Capabilities, + <<"authentication_failure_close">>) of + {bool, true} -> + {0, CloseMethod} = rabbit_binary_generator:map_exception( + 0, AmqpError, Protocol), + ok = send_on_channel0(State#v1.sock, CloseMethod, Protocol); + _ -> ok + end, + rabbit_misc:protocol_error(AmqpError); {protocol_error, Msg, Args} -> rabbit_misc:protocol_error(syntax_error, Msg, Args); {challenge, Challenge, AuthState1} -> -- cgit v1.2.1 From b5faad29d77f876640eb1247b49d088035ef2e2a Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 25 Sep 2013 10:01:06 +0100 Subject: Genericise network messages --- src/rabbit_direct.erl | 5 ++--- src/rabbit_reader.erl | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index 1577b0d5..5a004792 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -92,9 +92,8 @@ connect0(AuthFun, VHost, Protocol, Pid, Infos) -> true -> case AuthFun() of {ok, User} -> connect(User, VHost, Protocol, Pid, Infos); - {refused, Msg, Args} -> - Reason = io_lib:format(Msg, Args), - {error, {auth_failure, Reason}} + {refused, _M, _A} -> + {error, {auth_failure, "Refused"}} end; false -> {error, broker_not_found_on_node} end. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index bc82318a..11982778 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -978,8 +978,9 @@ auth_phase(Response, case rabbit_misc:table_lookup(Capabilities, <<"authentication_failure_close">>) of {bool, true} -> + AmqpError1 = AmqpError#amqp_error{explanation = "Refused"}, {0, CloseMethod} = rabbit_binary_generator:map_exception( - 0, AmqpError, Protocol), + 0, AmqpError1, Protocol), ok = send_on_channel0(State#v1.sock, CloseMethod, Protocol); _ -> ok end, -- cgit v1.2.1 From 251cb162aa49c1cfe485c3113dabfc64594d03aa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 26 Sep 2013 16:24:26 +0100 Subject: Shorten / simplify by abstracting over a duration-comparing predicate. --- src/rabbit_memory_monitor.erl | 59 ++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index 4f8202cd..13cda4ba 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -211,7 +211,8 @@ internal_update(State = #state{queue_durations = Durations, DesiredDurationAvg1 = desired_duration_average(State), State1 = State#state{desired_duration = DesiredDurationAvg1}, maybe_inform_queues( - DiskAlarm, DesiredDurationAvg, DesiredDurationAvg1, Durations), + should_inform_predicate(DiskAlarm), + DesiredDurationAvg, DesiredDurationAvg1, Durations), State1. desired_duration_average(#state{queue_duration_sum = Sum, @@ -236,52 +237,42 @@ desired_duration_average(#state{queue_duration_sum = Sum, (Sum / Count) / MemoryRatio end. -%% In normal use, we only inform queues immediately if the desired -%% duration has decreased, we want to ensure timely paging. -maybe_inform_queues(false, DesiredDurationAvg, DesiredDurationAvg1, +maybe_inform_queues(ShouldInform, DesiredDurationAvg, DesiredDurationAvg1, Durations) -> - case DesiredDurationAvg1 == infinity orelse - (DesiredDurationAvg /= infinity andalso - DesiredDurationAvg1 >= DesiredDurationAvg) of - true -> ok; - false -> inform_queues(DesiredDurationAvg1, Durations, - fun should_send/3) - end; -%% When the disk alarm has gone off though, we want to inform queues -%% immediately if the desired duration has *increased* - we want to -%% ensure timely stopping paging. -maybe_inform_queues(true, DesiredDurationAvg, DesiredDurationAvg1, - Durations) -> - case DesiredDurationAvg1 == infinity andalso - DesiredDurationAvg /= infinity of - true -> inform_queues(DesiredDurationAvg1, Durations, - fun should_send_disk_alarm/3); + case ShouldInform(DesiredDurationAvg, DesiredDurationAvg1) of + true -> inform_queues(ShouldInform, DesiredDurationAvg1, Durations); false -> ok end. -inform_queues(DesiredDurationAvg1, Durations, If) -> +inform_queues(ShouldInform, DesiredDurationAvg, Durations) -> true = ets:foldl( fun (Proc = #process{reported = QueueDuration, sent = PrevSendDuration, callback = {M, F, A}}, true) -> - case If(QueueDuration, PrevSendDuration, - DesiredDurationAvg1) of + case ShouldInform(PrevSendDuration, DesiredDurationAvg) orelse + ShouldInform(QueueDuration, DesiredDurationAvg) of true -> ok = erlang:apply( - M, F, A ++ [DesiredDurationAvg1]), + M, F, A ++ [DesiredDurationAvg]), ets:insert( Durations, - Proc#process{sent = DesiredDurationAvg1}); + Proc#process{sent = DesiredDurationAvg}); false -> true end end, true, Durations). - -should_send(infinity, infinity, _) -> true; -should_send(infinity, D, DD) -> DD < D; -should_send(D, infinity, DD) -> DD < D; -should_send(D1, D2, DD) -> DD < lists:min([D1, D2]). - -should_send_disk_alarm(_, infinity, _) -> false; -should_send_disk_alarm(_, _, infinity) -> true; -should_send_disk_alarm(_, _, _) -> false. +%% In normal use, we only inform queues immediately if the desired +%% duration has decreased, we want to ensure timely paging. +should_inform_predicate(false) -> fun (infinity, infinity) -> false; + (infinity, _D2) -> false; + (_D1, infinity) -> true; + (D1, D2) -> D1 < D2 + end; +%% When the disk alarm has gone off though, we want to inform queues +%% immediately if the desired duration has *increased* - we want to +%% ensure timely stopping paging. +should_inform_predicate(true) -> fun (infinity, infinity) -> false; + (infinity, _D2) -> true; + (_D1, infinity) -> false; + (D1, D2) -> D1 > D2 + end. -- cgit v1.2.1 From 81558d113d8e8af40d4995b18dbe48f914199cba Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 26 Sep 2013 16:54:50 +0100 Subject: Ahem. --- src/rabbit_memory_monitor.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index 13cda4ba..d34909a1 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -264,15 +264,15 @@ inform_queues(ShouldInform, DesiredDurationAvg, Durations) -> %% In normal use, we only inform queues immediately if the desired %% duration has decreased, we want to ensure timely paging. should_inform_predicate(false) -> fun (infinity, infinity) -> false; - (infinity, _D2) -> false; - (_D1, infinity) -> true; - (D1, D2) -> D1 < D2 + (infinity, _D2) -> true; + (_D1, infinity) -> false; + (D1, D2) -> D1 > D2 end; %% When the disk alarm has gone off though, we want to inform queues %% immediately if the desired duration has *increased* - we want to %% ensure timely stopping paging. should_inform_predicate(true) -> fun (infinity, infinity) -> false; - (infinity, _D2) -> true; - (_D1, infinity) -> false; - (D1, D2) -> D1 > D2 + (infinity, _D2) -> false; + (_D1, infinity) -> true; + (D1, D2) -> D1 < D2 end. -- cgit v1.2.1 From bbe39678de712bf1da9791d0056064f442c35428 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 26 Sep 2013 17:27:43 +0100 Subject: Ahem2. --- src/rabbit_memory_monitor.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index d34909a1..71a57511 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -250,8 +250,8 @@ inform_queues(ShouldInform, DesiredDurationAvg, Durations) -> fun (Proc = #process{reported = QueueDuration, sent = PrevSendDuration, callback = {M, F, A}}, true) -> - case ShouldInform(PrevSendDuration, DesiredDurationAvg) orelse - ShouldInform(QueueDuration, DesiredDurationAvg) of + case ShouldInform(PrevSendDuration, DesiredDurationAvg) + andalso ShouldInform(QueueDuration, DesiredDurationAvg) of true -> ok = erlang:apply( M, F, A ++ [DesiredDurationAvg]), ets:insert( -- cgit v1.2.1 From ae486ff8e84cf3f9c133ade03840157afe77fcda Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 26 Sep 2013 18:07:54 +0100 Subject: Just one predicate. --- src/rabbit_memory_monitor.erl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index 71a57511..66c01298 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -263,16 +263,13 @@ inform_queues(ShouldInform, DesiredDurationAvg, Durations) -> %% In normal use, we only inform queues immediately if the desired %% duration has decreased, we want to ensure timely paging. -should_inform_predicate(false) -> fun (infinity, infinity) -> false; - (infinity, _D2) -> true; - (_D1, infinity) -> false; - (D1, D2) -> D1 > D2 - end; +should_inform_predicate(false) -> fun greater_than/2; %% When the disk alarm has gone off though, we want to inform queues %% immediately if the desired duration has *increased* - we want to %% ensure timely stopping paging. -should_inform_predicate(true) -> fun (infinity, infinity) -> false; - (infinity, _D2) -> false; - (_D1, infinity) -> true; - (D1, D2) -> D1 < D2 - end. +should_inform_predicate(true) -> fun (D1, D2) -> greater_than(D2, D1) end. + +greater_than(infinity, infinity) -> false; +greater_than(infinity, _D2) -> true; +greater_than(_D1, infinity) -> false; +greater_than(D1, D2) -> D1 > D2. -- cgit v1.2.1 From 13c9c3f4ffc0592a91e7d1f75df015ea42902205 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 26 Sep 2013 18:20:29 +0100 Subject: inlining --- src/rabbit_memory_monitor.erl | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index 66c01298..62b464e0 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -209,11 +209,12 @@ internal_update(State = #state{queue_durations = Durations, desired_duration = DesiredDurationAvg, disk_alarm = DiskAlarm}) -> DesiredDurationAvg1 = desired_duration_average(State), - State1 = State#state{desired_duration = DesiredDurationAvg1}, - maybe_inform_queues( - should_inform_predicate(DiskAlarm), - DesiredDurationAvg, DesiredDurationAvg1, Durations), - State1. + ShouldInform = should_inform_predicate(DiskAlarm), + case ShouldInform(DesiredDurationAvg, DesiredDurationAvg1) of + true -> inform_queues(ShouldInform, DesiredDurationAvg1, Durations); + false -> ok + end, + State#state{desired_duration = DesiredDurationAvg1}. desired_duration_average(#state{queue_duration_sum = Sum, queue_duration_count = Count, @@ -237,13 +238,6 @@ desired_duration_average(#state{queue_duration_sum = Sum, (Sum / Count) / MemoryRatio end. -maybe_inform_queues(ShouldInform, DesiredDurationAvg, DesiredDurationAvg1, - Durations) -> - case ShouldInform(DesiredDurationAvg, DesiredDurationAvg1) of - true -> inform_queues(ShouldInform, DesiredDurationAvg1, Durations); - false -> ok - end. - inform_queues(ShouldInform, DesiredDurationAvg, Durations) -> true = ets:foldl( -- cgit v1.2.1 From eb7fd23916133584ee6d93e612e80972a48406ae Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 26 Sep 2013 18:24:18 +0100 Subject: minor refactor --- src/rabbit_memory_monitor.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index 62b464e0..4bd1a575 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -216,9 +216,11 @@ internal_update(State = #state{queue_durations = Durations, end, State#state{desired_duration = DesiredDurationAvg1}. -desired_duration_average(#state{queue_duration_sum = Sum, - queue_duration_count = Count, - disk_alarm = DiskAlarm}) -> +desired_duration_average(#state{disk_alarm = true}) -> + infinity; +desired_duration_average(#state{disk_alarm = false, + queue_duration_sum = Sum, + queue_duration_count = Count}) -> {ok, LimitThreshold} = application:get_env(rabbit, vm_memory_high_watermark_paging_ratio), MemoryLimit = vm_memory_monitor:get_memory_limit(), @@ -226,9 +228,7 @@ desired_duration_average(#state{queue_duration_sum = Sum, true -> erlang:memory(total) / MemoryLimit; false -> infinity end, - if DiskAlarm -> - infinity; - MemoryRatio =:= infinity -> + if MemoryRatio =:= infinity -> 0.0; MemoryRatio < LimitThreshold orelse Count == 0 -> infinity; -- cgit v1.2.1 From 81a3ada337243110ef1f56325a97426048bc8d69 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 1 Oct 2013 15:27:00 +0100 Subject: Return verbose message when refusing to authenticate --- src/rabbit_reader.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 11982778..157b8270 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -978,7 +978,11 @@ auth_phase(Response, case rabbit_misc:table_lookup(Capabilities, <<"authentication_failure_close">>) of {bool, true} -> - AmqpError1 = AmqpError#amqp_error{explanation = "Refused"}, + SafeMsg = io_lib:format( + "Login was refused using authentication " + "mechanism ~s. For details see the broker " + "logfile.", [Name]), + AmqpError1 = AmqpError#amqp_error{explanation = SafeMsg}, {0, CloseMethod} = rabbit_binary_generator:map_exception( 0, AmqpError1, Protocol), ok = send_on_channel0(State#v1.sock, CloseMethod, Protocol); -- cgit v1.2.1 From 4778b08097c8291524140444c8e872968fa2485a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 4 Oct 2013 14:43:15 +0100 Subject: Introduce flow controlled versions of send_command() for the Erlang client to use. --- src/rabbit_writer.erl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src') diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index bf6964d8..34dd3d3b 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -25,6 +25,7 @@ -export([send_command/2, send_command/3, send_command_sync/2, send_command_sync/3, send_command_and_notify/4, send_command_and_notify/5, + send_command_flow/2, send_command_flow/3, flush/1]). -export([internal_send_command/4, internal_send_command/6]). @@ -78,6 +79,11 @@ (pid(), pid(), pid(), rabbit_framing:amqp_method_record(), rabbit_types:content()) -> 'ok'). +-spec(send_command_flow/2 :: + (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). +-spec(send_command_flow/3 :: + (pid(), rabbit_framing:amqp_method_record(), rabbit_types:content()) + -> 'ok'). -spec(flush/1 :: (pid()) -> 'ok'). -spec(internal_send_command/4 :: (rabbit_net:socket(), rabbit_channel:channel_number(), @@ -165,6 +171,12 @@ handle_message({send_command, MethodRecord}, State) -> internal_send_command_async(MethodRecord, State); handle_message({send_command, MethodRecord, Content}, State) -> internal_send_command_async(MethodRecord, Content, State); +handle_message({send_command_flow, MethodRecord, Sender}, State) -> + credit_flow:ack(Sender), + internal_send_command_async(MethodRecord, State); +handle_message({send_command_flow, MethodRecord, Content, Sender}, State) -> + credit_flow:ack(Sender), + internal_send_command_async(MethodRecord, Content, State); handle_message({'$gen_call', From, {send_command_sync, MethodRecord}}, State) -> State1 = internal_flush( internal_send_command_async(MethodRecord, State)), @@ -212,6 +224,16 @@ send_command(W, MethodRecord, Content) -> W ! {send_command, MethodRecord, Content}, ok. +send_command_flow(W, MethodRecord) -> + credit_flow:send(W), + W ! {send_command_flow, MethodRecord, self()}, + ok. + +send_command_flow(W, MethodRecord, Content) -> + credit_flow:send(W), + W ! {send_command_flow, MethodRecord, Content, self()}, + ok. + send_command_sync(W, MethodRecord) -> call(W, {send_command_sync, MethodRecord}). -- cgit v1.2.1 From ab20cb6125e2fb9ed1ac156f51784a11ff01ad5a Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 8 Oct 2013 17:30:06 +0100 Subject: Support config-supplied verify_fun in tuple-format --- src/rabbit_networking.erl | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 46cfabe3..aa54d6c5 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -149,14 +149,23 @@ ensure_ssl() -> ok = app_utils:start_applications(SslAppsConfig), {ok, SslOptsConfig} = application:get_env(rabbit, ssl_options), - % unknown_ca errors are silently ignored prior to R14B unless we - % supply this verify_fun - remove when at least R14B is required - case proplists:get_value(verify, SslOptsConfig, verify_none) of - verify_none -> SslOptsConfig; - verify_peer -> [{verify_fun, fun([]) -> true; - ([_|_]) -> false - end} - | SslOptsConfig] + case rabbit_misc:pget(verify_fun, SslOptsConfig) of + {Module, Function} -> + rabbit_misc:pset(verify_fun, + fun (OtpCert, Event, State) -> + apply(Module, Function, + [OtpCert, Event, State]) + end, SslOptsConfig); + undefined -> + % unknown_ca errors are silently ignored prior to R14B unless we + % supply this verify_fun - remove when at least R14B is required + case proplists:get_value(verify, SslOptsConfig, verify_none) of + verify_none -> SslOptsConfig; + verify_peer -> [{verify_fun, fun([]) -> true; + ([_|_]) -> false + end} + | SslOptsConfig] + end end. ssl_transform_fun(SslOpts) -> -- cgit v1.2.1 From 264c96cfd0dbe70ad35e770cf66c4b06c56904b3 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 9 Oct 2013 11:29:59 +0100 Subject: Support only historical verify_fun signature --- src/rabbit_networking.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index aa54d6c5..47b47e42 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -152,9 +152,8 @@ ensure_ssl() -> case rabbit_misc:pget(verify_fun, SslOptsConfig) of {Module, Function} -> rabbit_misc:pset(verify_fun, - fun (OtpCert, Event, State) -> - apply(Module, Function, - [OtpCert, Event, State]) + fun (ErrorList) -> + apply(Module, Function, [ErrorList]) end, SslOptsConfig); undefined -> % unknown_ca errors are silently ignored prior to R14B unless we -- cgit v1.2.1 From e11db42252041c6bb7f3fa6b2b50bdb64a3a67bf Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 9 Oct 2013 11:57:50 +0100 Subject: erlang:apply is not necessary since the arguments are known --- src/rabbit_networking.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 47b47e42..91be4dcb 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -153,7 +153,7 @@ ensure_ssl() -> {Module, Function} -> rabbit_misc:pset(verify_fun, fun (ErrorList) -> - apply(Module, Function, [ErrorList]) + Module:Function(ErrorList) end, SslOptsConfig); undefined -> % unknown_ca errors are silently ignored prior to R14B unless we -- cgit v1.2.1 From 83fa3336f9e97549907fabb2a59a1ef29ae3cf46 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 9 Oct 2013 16:22:54 +0100 Subject: introduce another intermediate supervisor into the connection tree --- src/rabbit_connection_helper_sup.erl | 40 ++++++++++++++++++++++++++++++++++++ src/rabbit_connection_sup.erl | 7 ++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/rabbit_connection_helper_sup.erl (limited to 'src') diff --git a/src/rabbit_connection_helper_sup.erl b/src/rabbit_connection_helper_sup.erl new file mode 100644 index 00000000..580aa65f --- /dev/null +++ b/src/rabbit_connection_helper_sup.erl @@ -0,0 +1,40 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. +%% + +-module(rabbit_connection_helper_sup). + +-behaviour(supervisor2). + +-export([start_link/0]). +-export([init/1]). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). +-spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). +-endif. + +%%---------------------------------------------------------------------------- + +start_link() -> + supervisor2:start_link(?MODULE, []). + +%%-------------------------------------------------------------------------- + +init([]) -> + {ok, {{one_for_all, 0, 1}, []}}. + + diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index c1fa17aa..d5969873 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -52,12 +52,17 @@ start_link() -> SupPid, {channel_sup3, {rabbit_intermediate_sup, start_link, []}, intrinsic, infinity, supervisor, [rabbit_intermediate_sup]}), + {ok, ConHelperSupPid} = + supervisor2:start_child( + SupPid, + {helper_sup, {rabbit_connection_helper_sup, start_link, []}, + intrinsic, infinity, supervisor, [rabbit_connection_helper_sup]}), {ok, ReaderPid} = supervisor2:start_child( SupPid, {reader, {rabbit_reader, start_link, [ChannelSup3Pid, Collector, - rabbit_heartbeat:start_heartbeat_fun(SupPid)]}, + rabbit_heartbeat:start_heartbeat_fun(ConHelperSupPid)]}, intrinsic, ?MAX_WAIT, worker, [rabbit_reader]}), {ok, SupPid, ReaderPid}. -- cgit v1.2.1 From 43eaf1fd45f55a7cfaf9618bc3622982d03109d5 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 9 Oct 2013 16:45:39 +0100 Subject: Defer starting a queue collector until we've received tune-ok By waiting for the connection to be fully opened, we reduce resource usage for abandoned connections and DoS vectors. The collector is started as a child of the connection_helper_sup, thus avoiding a potential deadlock with the parent supervisor during shutdown. --- src/rabbit_connection_helper_sup.erl | 10 ++++++++++ src/rabbit_connection_sup.erl | 7 +------ src/rabbit_reader.erl | 21 +++++++++++++-------- 3 files changed, 24 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/rabbit_connection_helper_sup.erl b/src/rabbit_connection_helper_sup.erl index 580aa65f..8f6c7698 100644 --- a/src/rabbit_connection_helper_sup.erl +++ b/src/rabbit_connection_helper_sup.erl @@ -19,12 +19,16 @@ -behaviour(supervisor2). -export([start_link/0]). +-export([start_queue_collector/1]). -export([init/1]). +-include("rabbit.hrl"). + %%---------------------------------------------------------------------------- -ifdef(use_specs). -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). +-spec(start_queue_collector/1 :: (pid()) -> rabbit_types:ok_pid_or_error()). -endif. %%---------------------------------------------------------------------------- @@ -32,6 +36,12 @@ start_link() -> supervisor2:start_link(?MODULE, []). +start_queue_collector(SupPid) -> + supervisor2:start_child( + SupPid, + {collector, {rabbit_queue_collector, start_link, []}, + intrinsic, ?MAX_WAIT, worker, [rabbit_queue_collector]}). + %%-------------------------------------------------------------------------- init([]) -> diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index d5969873..1f4ab19c 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -37,11 +37,6 @@ start_link() -> {ok, SupPid} = supervisor2:start_link(?MODULE, []), - {ok, Collector} = - supervisor2:start_child( - SupPid, - {collector, {rabbit_queue_collector, start_link, []}, - intrinsic, ?MAX_WAIT, worker, [rabbit_queue_collector]}), %% We need to get channels in the hierarchy here so they get shut %% down after the reader, so the reader gets a chance to terminate %% them cleanly. But for 1.0 readers we can't start the real @@ -61,7 +56,7 @@ start_link() -> supervisor2:start_child( SupPid, {reader, {rabbit_reader, start_link, - [ChannelSup3Pid, Collector, + [ChannelSup3Pid, ConHelperSupPid, rabbit_heartbeat:start_heartbeat_fun(ConHelperSupPid)]}, intrinsic, ?MAX_WAIT, worker, [rabbit_reader]}), {ok, SupPid, ReaderPid}. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 157b8270..9e81511a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -36,8 +36,8 @@ %%-------------------------------------------------------------------------- -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, - connection_state, queue_collector, heartbeater, stats_timer, - ch_sup3_pid, channel_sup_sup_pid, start_heartbeat_fun, + connection_state, helper_sup_pid, queue_collector, heartbeater, + stats_timer, ch_sup3_pid, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, throttle}). -record(connection, {name, host, peer_host, port, peer_port, @@ -104,19 +104,19 @@ %%-------------------------------------------------------------------------- -start_link(ChannelSup3Pid, Collector, StartHeartbeatFun) -> +start_link(ChannelSup3Pid, HelperPid, StartHeartbeatFun) -> {ok, proc_lib:spawn_link(?MODULE, init, [self(), ChannelSup3Pid, - Collector, StartHeartbeatFun])}. + HelperPid, StartHeartbeatFun])}. shutdown(Pid, Explanation) -> gen_server:call(Pid, {shutdown, Explanation}, infinity). -init(Parent, ChSup3Pid, Collector, StartHeartbeatFun) -> +init(Parent, ChSup3Pid, HelperPid, StartHeartbeatFun) -> Deb = sys:debug_options([]), receive {go, Sock, SockTransform} -> start_connection( - Parent, ChSup3Pid, Collector, StartHeartbeatFun, Deb, Sock, + Parent, ChSup3Pid, HelperPid, StartHeartbeatFun, Deb, Sock, SockTransform) end. @@ -205,7 +205,7 @@ socket_op(Sock, Fun) -> exit(normal) end. -start_connection(Parent, ChSup3Pid, Collector, StartHeartbeatFun, Deb, +start_connection(Parent, ChSup3Pid, HelperPid, StartHeartbeatFun, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), Name = case rabbit_net:connection_string(Sock, inbound) of @@ -242,7 +242,8 @@ start_connection(Parent, ChSup3Pid, Collector, StartHeartbeatFun, Deb, recv_len = 0, pending_recv = false, connection_state = pre_init, - queue_collector = Collector, + queue_collector = undefined, %% started on tune-ok + helper_sup_pid = HelperPid, heartbeater = none, ch_sup3_pid = ChSup3Pid, channel_sup_sup_pid = none, @@ -851,6 +852,7 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, heartbeat = ClientHeartbeat}, State = #v1{connection_state = tuning, connection = Connection, + helper_sup_pid = HelperPid, sock = Sock, start_heartbeat_fun = SHF}) -> ServerFrameMax = server_frame_max(), @@ -863,6 +865,8 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, not_allowed, "frame_max=~w > ~w max size", [FrameMax, ServerFrameMax]); true -> + {ok, Collector} = + rabbit_connection_helper_sup:start_queue_collector(HelperPid), Frame = rabbit_binary_generator:build_heartbeat_frame(), SendFun = fun() -> catch rabbit_net:send(Sock, Frame) end, Parent = self(), @@ -873,6 +877,7 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, connection = Connection#connection{ timeout_sec = ClientHeartbeat, frame_max = FrameMax}, + queue_collector = Collector, heartbeater = Heartbeater} end; -- cgit v1.2.1 From c48fa989303bd235f16c60168988818426a7d7c0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 10 Oct 2013 10:40:54 +0100 Subject: Unify the intermediate supervision tree under r_connection_helper_sup This deprecates r_intermediate_sup and transitions the chan_sup_sup and queue collector processes into the helper sup, as well as providing an API for starting both. --- src/rabbit_connection_helper_sup.erl | 16 +++++++++--- src/rabbit_connection_sup.erl | 18 +++++++------- src/rabbit_intermediate_sup.erl | 39 ----------------------------- src/rabbit_reader.erl | 48 +++++++++++++++++------------------- 4 files changed, 43 insertions(+), 78 deletions(-) delete mode 100644 src/rabbit_intermediate_sup.erl (limited to 'src') diff --git a/src/rabbit_connection_helper_sup.erl b/src/rabbit_connection_helper_sup.erl index 8f6c7698..e51615e8 100644 --- a/src/rabbit_connection_helper_sup.erl +++ b/src/rabbit_connection_helper_sup.erl @@ -19,7 +19,9 @@ -behaviour(supervisor2). -export([start_link/0]). --export([start_queue_collector/1]). +-export([start_channel_sup_sup/1, + start_queue_collector/1]). + -export([init/1]). -include("rabbit.hrl"). @@ -28,6 +30,7 @@ -ifdef(use_specs). -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). +-spec(start_channel_sup_sup/1 :: (pid()) -> rabbit_types:ok_pid_or_error()). -spec(start_queue_collector/1 :: (pid()) -> rabbit_types:ok_pid_or_error()). -endif. @@ -36,15 +39,20 @@ start_link() -> supervisor2:start_link(?MODULE, []). +start_channel_sup_sup(SupPid) -> + supervisor2:start_child( + SupPid, + {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, + intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}). + start_queue_collector(SupPid) -> supervisor2:start_child( SupPid, {collector, {rabbit_queue_collector, start_link, []}, intrinsic, ?MAX_WAIT, worker, [rabbit_queue_collector]}). -%%-------------------------------------------------------------------------- +%%---------------------------------------------------------------------------- init([]) -> - {ok, {{one_for_all, 0, 1}, []}}. - + {ok, {{one_for_one, 10, 10}, []}}. diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index 1f4ab19c..0c6dce76 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -42,22 +42,22 @@ start_link() -> %% them cleanly. But for 1.0 readers we can't start the real %% ch_sup_sup (because we don't know if we will be 0-9-1 or 1.0) - %% so we add another supervisor into the hierarchy. - {ok, ChannelSup3Pid} = + %% + %% This supervisor also acts as an intermediary for heartbeaters and + %% the queue collector process, since these must not be siblings of the + %% reader due to the potential for deadlock if they are added/restarted + %% whilst the supervision tree is shutting down. + {ok, IntermediateSup} = supervisor2:start_child( SupPid, - {channel_sup3, {rabbit_intermediate_sup, start_link, []}, - intrinsic, infinity, supervisor, [rabbit_intermediate_sup]}), - {ok, ConHelperSupPid} = - supervisor2:start_child( - SupPid, - {helper_sup, {rabbit_connection_helper_sup, start_link, []}, + {channel_sup3, {rabbit_connection_helper_sup, start_link, []}, intrinsic, infinity, supervisor, [rabbit_connection_helper_sup]}), {ok, ReaderPid} = supervisor2:start_child( SupPid, {reader, {rabbit_reader, start_link, - [ChannelSup3Pid, ConHelperSupPid, - rabbit_heartbeat:start_heartbeat_fun(ConHelperSupPid)]}, + [IntermediateSup, + rabbit_heartbeat:start_heartbeat_fun(IntermediateSup)]}, intrinsic, ?MAX_WAIT, worker, [rabbit_reader]}), {ok, SupPid, ReaderPid}. diff --git a/src/rabbit_intermediate_sup.erl b/src/rabbit_intermediate_sup.erl deleted file mode 100644 index a9381f20..00000000 --- a/src/rabbit_intermediate_sup.erl +++ /dev/null @@ -1,39 +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 Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2013-2013 GoPivotal, Inc. All rights reserved. -%% - --module(rabbit_intermediate_sup). - --behaviour(supervisor2). - --export([start_link/0]). - --export([init/1]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). --spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --endif. - -%%---------------------------------------------------------------------------- - -start_link() -> - supervisor2:start_link(?MODULE, []). - -%%---------------------------------------------------------------------------- - -init([]) -> - {ok, {{one_for_one, 10, 10}, []}}. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 9e81511a..574bf882 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -18,12 +18,12 @@ -include("rabbit_framing.hrl"). -include("rabbit.hrl"). --export([start_link/3, info_keys/0, info/1, info/2, force_event_refresh/1, +-export([start_link/2, info_keys/0, info/1, info/2, force_event_refresh/1, shutdown/2]). -export([system_continue/3, system_terminate/4, system_code_change/4]). --export([init/4, mainloop/2, recvloop/2]). +-export([init/3, mainloop/2, recvloop/2]). -export([conserve_resources/3, server_properties/1]). @@ -36,8 +36,8 @@ %%-------------------------------------------------------------------------- -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, - connection_state, helper_sup_pid, queue_collector, heartbeater, - stats_timer, ch_sup3_pid, channel_sup_sup_pid, start_heartbeat_fun, + connection_state, helper_sup, queue_collector, heartbeater, + stats_timer, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, throttle}). -record(connection, {name, host, peer_host, port, peer_port, @@ -74,7 +74,7 @@ -ifdef(use_specs). --spec(start_link/3 :: (pid(), pid(), rabbit_heartbeat:start_heartbeat_fun()) -> +-spec(start_link/2 :: (pid(), rabbit_heartbeat:start_heartbeat_fun()) -> rabbit_types:ok(pid())). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (pid()) -> rabbit_types:infos()). @@ -86,10 +86,10 @@ rabbit_framing:amqp_table()). %% These specs only exists to add no_return() to keep dialyzer happy --spec(init/4 :: (pid(), pid(), pid(), rabbit_heartbeat:start_heartbeat_fun()) +-spec(init/3 :: (pid(), pid(), rabbit_heartbeat:start_heartbeat_fun()) -> no_return()). --spec(start_connection/7 :: - (pid(), pid(), pid(), rabbit_heartbeat:start_heartbeat_fun(), any(), +-spec(start_connection/6 :: + (pid(), pid(), rabbit_heartbeat:start_heartbeat_fun(), any(), rabbit_net:socket(), fun ((rabbit_net:socket()) -> rabbit_types:ok_or_error2( @@ -104,19 +104,19 @@ %%-------------------------------------------------------------------------- -start_link(ChannelSup3Pid, HelperPid, StartHeartbeatFun) -> - {ok, proc_lib:spawn_link(?MODULE, init, [self(), ChannelSup3Pid, - HelperPid, StartHeartbeatFun])}. +start_link(HelperSup, StartHeartbeatFun) -> + {ok, proc_lib:spawn_link(?MODULE, init, [self(), HelperSup, + StartHeartbeatFun])}. shutdown(Pid, Explanation) -> gen_server:call(Pid, {shutdown, Explanation}, infinity). -init(Parent, ChSup3Pid, HelperPid, StartHeartbeatFun) -> +init(Parent, HelperSup, StartHeartbeatFun) -> Deb = sys:debug_options([]), receive {go, Sock, SockTransform} -> start_connection( - Parent, ChSup3Pid, HelperPid, StartHeartbeatFun, Deb, Sock, + Parent, HelperSup, StartHeartbeatFun, Deb, Sock, SockTransform) end. @@ -205,8 +205,8 @@ socket_op(Sock, Fun) -> exit(normal) end. -start_connection(Parent, ChSup3Pid, HelperPid, StartHeartbeatFun, Deb, - Sock, SockTransform) -> +start_connection(Parent, HelperSup, StartHeartbeatFun, + Deb, Sock, SockTransform) -> process_flag(trap_exit, true), Name = case rabbit_net:connection_string(Sock, inbound) of {ok, Str} -> Str; @@ -243,9 +243,8 @@ start_connection(Parent, ChSup3Pid, HelperPid, StartHeartbeatFun, Deb, pending_recv = false, connection_state = pre_init, queue_collector = undefined, %% started on tune-ok - helper_sup_pid = HelperPid, + helper_sup = HelperSup, heartbeater = none, - ch_sup3_pid = ChSup3Pid, channel_sup_sup_pid = none, start_heartbeat_fun = StartHeartbeatFun, buf = [], @@ -852,7 +851,7 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, heartbeat = ClientHeartbeat}, State = #v1{connection_state = tuning, connection = Connection, - helper_sup_pid = HelperPid, + helper_sup = SupPid, sock = Sock, start_heartbeat_fun = SHF}) -> ServerFrameMax = server_frame_max(), @@ -866,7 +865,7 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, [FrameMax, ServerFrameMax]); true -> {ok, Collector} = - rabbit_connection_helper_sup:start_queue_collector(HelperPid), + rabbit_connection_helper_sup:start_queue_collector(SupPid), Frame = rabbit_binary_generator:build_heartbeat_frame(), SendFun = fun() -> catch rabbit_net:send(Sock, Frame) end, Parent = self(), @@ -886,7 +885,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, connection = Connection = #connection{ user = User, protocol = Protocol}, - ch_sup3_pid = ChSup3Pid, + helper_sup = SupPid, sock = Sock, throttle = Throttle}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), @@ -895,10 +894,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, Conserve = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}), Throttle1 = Throttle#throttle{alarmed_by = Conserve}, {ok, ChannelSupSupPid} = - supervisor2:start_child( - ChSup3Pid, - {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, - intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), + rabbit_connection_helper_sup:start_channel_sup_sup(SupPid), State1 = control_throttle( State#v1{connection_state = running, connection = NewConnection, @@ -1112,9 +1108,9 @@ pack_for_1_0(#v1{parent = Parent, recv_len = RecvLen, pending_recv = PendingRecv, queue_collector = QueueCollector, - ch_sup3_pid = ChSup3Pid, + helper_sup = SupPid, start_heartbeat_fun = SHF, buf = Buf, buf_len = BufLen}) -> - {Parent, Sock, RecvLen, PendingRecv, QueueCollector, ChSup3Pid, SHF, + {Parent, Sock, RecvLen, PendingRecv, QueueCollector, SupPid, SHF, Buf, BufLen}. -- cgit v1.2.1 From 42d7a8154cbfe74c0e626fdc43499dfa0a4f570f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 10 Oct 2013 15:15:05 +0100 Subject: Introduce rabbit_heartbeat:start/6 API --- src/rabbit_connection_sup.erl | 4 +--- src/rabbit_heartbeat.erl | 17 ++++++++++++++++ src/rabbit_reader.erl | 45 ++++++++++++++++--------------------------- 3 files changed, 35 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index 0c6dce76..ebdab666 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -55,9 +55,7 @@ start_link() -> {ok, ReaderPid} = supervisor2:start_child( SupPid, - {reader, {rabbit_reader, start_link, - [IntermediateSup, - rabbit_heartbeat:start_heartbeat_fun(IntermediateSup)]}, + {reader, {rabbit_reader, start_link, [IntermediateSup]}, intrinsic, ?MAX_WAIT, worker, [rabbit_reader]}), {ok, SupPid, ReaderPid}. diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl index fac74edb..eefab3bc 100644 --- a/src/rabbit_heartbeat.erl +++ b/src/rabbit_heartbeat.erl @@ -16,6 +16,7 @@ -module(rabbit_heartbeat). +-export([start/6]). -export([start_heartbeat_sender/3, start_heartbeat_receiver/3, start_heartbeat_fun/1, pause_monitor/1, resume_monitor/1]). @@ -39,6 +40,11 @@ non_neg_integer(), heartbeat_callback()) -> no_return())). +-spec(start/6 :: + (pid(), rabbit_net:socket(), + non_neg_integer(), heartbeat_callback(), + non_neg_integer(), heartbeat_callback()) -> heartbeaters()). + -spec(start_heartbeat_sender/3 :: (rabbit_net:socket(), non_neg_integer(), heartbeat_callback()) -> rabbit_types:ok(pid())). @@ -61,6 +67,17 @@ %%---------------------------------------------------------------------------- +start(SupPid, Sock, SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) -> + {ok, Sender} = + start_heartbeater(SendTimeoutSec, SupPid, Sock, + SendFun, heartbeat_sender, + start_heartbeat_sender), + {ok, Receiver} = + start_heartbeater(ReceiveTimeoutSec, SupPid, Sock, + ReceiveFun, heartbeat_receiver, + start_heartbeat_receiver), + {Sender, Receiver}. + start_heartbeat_sender(Sock, TimeoutSec, SendFun) -> %% the 'div 2' is there so that we don't end up waiting for nearly %% 2 * TimeoutSec before sending a heartbeat in the boundary case diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 574bf882..47eb603f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -18,12 +18,12 @@ -include("rabbit_framing.hrl"). -include("rabbit.hrl"). --export([start_link/2, info_keys/0, info/1, info/2, force_event_refresh/1, +-export([start_link/1, info_keys/0, info/1, info/2, force_event_refresh/1, shutdown/2]). -export([system_continue/3, system_terminate/4, system_code_change/4]). --export([init/3, mainloop/2, recvloop/2]). +-export([init/2, mainloop/2, recvloop/2]). -export([conserve_resources/3, server_properties/1]). @@ -37,8 +37,7 @@ -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, connection_state, helper_sup, queue_collector, heartbeater, - stats_timer, channel_sup_sup_pid, start_heartbeat_fun, - buf, buf_len, throttle}). + stats_timer, channel_sup_sup_pid, buf, buf_len, throttle}). -record(connection, {name, host, peer_host, port, peer_port, protocol, user, timeout_sec, frame_max, vhost, @@ -74,8 +73,7 @@ -ifdef(use_specs). --spec(start_link/2 :: (pid(), rabbit_heartbeat:start_heartbeat_fun()) -> - rabbit_types:ok(pid())). +-spec(start_link/1 :: (pid()) -> rabbit_types:ok(pid())). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (pid()) -> rabbit_types:infos()). -spec(info/2 :: (pid(), rabbit_types:info_keys()) -> rabbit_types:infos()). @@ -86,11 +84,9 @@ rabbit_framing:amqp_table()). %% These specs only exists to add no_return() to keep dialyzer happy --spec(init/3 :: (pid(), pid(), rabbit_heartbeat:start_heartbeat_fun()) - -> no_return()). --spec(start_connection/6 :: - (pid(), pid(), rabbit_heartbeat:start_heartbeat_fun(), any(), - rabbit_net:socket(), +-spec(init/2 :: (pid(), pid()) -> no_return()). +-spec(start_connection/5 :: + (pid(), pid(), any(), rabbit_net:socket(), fun ((rabbit_net:socket()) -> rabbit_types:ok_or_error2( rabbit_net:socket(), any()))) -> no_return()). @@ -104,20 +100,17 @@ %%-------------------------------------------------------------------------- -start_link(HelperSup, StartHeartbeatFun) -> - {ok, proc_lib:spawn_link(?MODULE, init, [self(), HelperSup, - StartHeartbeatFun])}. +start_link(HelperSup) -> + {ok, proc_lib:spawn_link(?MODULE, init, [self(), HelperSup])}. shutdown(Pid, Explanation) -> gen_server:call(Pid, {shutdown, Explanation}, infinity). -init(Parent, HelperSup, StartHeartbeatFun) -> +init(Parent, HelperSup) -> Deb = sys:debug_options([]), receive {go, Sock, SockTransform} -> - start_connection( - Parent, HelperSup, StartHeartbeatFun, Deb, Sock, - SockTransform) + start_connection(Parent, HelperSup, Deb, Sock, SockTransform) end. system_continue(Parent, Deb, State) -> @@ -205,8 +198,7 @@ socket_op(Sock, Fun) -> exit(normal) end. -start_connection(Parent, HelperSup, StartHeartbeatFun, - Deb, Sock, SockTransform) -> +start_connection(Parent, HelperSup, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), Name = case rabbit_net:connection_string(Sock, inbound) of {ok, Str} -> Str; @@ -246,7 +238,6 @@ start_connection(Parent, HelperSup, StartHeartbeatFun, helper_sup = HelperSup, heartbeater = none, channel_sup_sup_pid = none, - start_heartbeat_fun = StartHeartbeatFun, buf = [], buf_len = 0, throttle = #throttle{ @@ -852,8 +843,7 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, State = #v1{connection_state = tuning, connection = Connection, helper_sup = SupPid, - sock = Sock, - start_heartbeat_fun = SHF}) -> + sock = Sock}) -> ServerFrameMax = server_frame_max(), if FrameMax /= 0 andalso FrameMax < ?FRAME_MIN_SIZE -> rabbit_misc:protocol_error( @@ -870,8 +860,9 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, SendFun = fun() -> catch rabbit_net:send(Sock, Frame) end, Parent = self(), ReceiveFun = fun() -> Parent ! heartbeat_timeout end, - Heartbeater = SHF(Sock, ClientHeartbeat, SendFun, - ClientHeartbeat, ReceiveFun), + Heartbeater = + rabbit_heartbeat:start(SupPid, Sock, ClientHeartbeat, + SendFun, ClientHeartbeat, ReceiveFun), State#v1{connection_state = opening, connection = Connection#connection{ timeout_sec = ClientHeartbeat, @@ -1109,8 +1100,6 @@ pack_for_1_0(#v1{parent = Parent, pending_recv = PendingRecv, queue_collector = QueueCollector, helper_sup = SupPid, - start_heartbeat_fun = SHF, buf = Buf, buf_len = BufLen}) -> - {Parent, Sock, RecvLen, PendingRecv, QueueCollector, SupPid, SHF, - Buf, BufLen}. + {Parent, Sock, RecvLen, PendingRecv, QueueCollector, SupPid, Buf, BufLen}. -- cgit v1.2.1 From 56f2553dc3e21c7a5f4434f6632c3c9d7effe42c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 10 Oct 2013 16:56:05 +0100 Subject: ...and remove the old API. --- src/rabbit_heartbeat.erl | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) (limited to 'src') diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl index eefab3bc..ca67254b 100644 --- a/src/rabbit_heartbeat.erl +++ b/src/rabbit_heartbeat.erl @@ -18,7 +18,7 @@ -export([start/6]). -export([start_heartbeat_sender/3, start_heartbeat_receiver/3, - start_heartbeat_fun/1, pause_monitor/1, resume_monitor/1]). + pause_monitor/1, resume_monitor/1]). -export([system_continue/3, system_terminate/4, system_code_change/4]). @@ -29,17 +29,11 @@ -ifdef(use_specs). -export_type([heartbeaters/0]). --export_type([start_heartbeat_fun/0]). -type(heartbeaters() :: {rabbit_types:maybe(pid()), rabbit_types:maybe(pid())}). -type(heartbeat_callback() :: fun (() -> any())). --type(start_heartbeat_fun() :: - fun((rabbit_net:socket(), non_neg_integer(), heartbeat_callback(), - non_neg_integer(), heartbeat_callback()) -> - no_return())). - -spec(start/6 :: (pid(), rabbit_net:socket(), non_neg_integer(), heartbeat_callback(), @@ -52,10 +46,6 @@ (rabbit_net:socket(), non_neg_integer(), heartbeat_callback()) -> rabbit_types:ok(pid())). --spec(start_heartbeat_fun/1 :: - (pid()) -> start_heartbeat_fun()). - - -spec(pause_monitor/1 :: (heartbeaters()) -> 'ok'). -spec(resume_monitor/1 :: (heartbeaters()) -> 'ok'). @@ -92,19 +82,6 @@ start_heartbeat_receiver(Sock, TimeoutSec, ReceiveFun) -> heartbeater({Sock, TimeoutSec * 1000, recv_oct, 1, fun () -> ReceiveFun(), stop end}). -start_heartbeat_fun(SupPid) -> - fun (Sock, SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) -> - {ok, Sender} = - start_heartbeater(SendTimeoutSec, SupPid, Sock, - SendFun, heartbeat_sender, - start_heartbeat_sender), - {ok, Receiver} = - start_heartbeater(ReceiveTimeoutSec, SupPid, Sock, - ReceiveFun, heartbeat_receiver, - start_heartbeat_receiver), - {Sender, Receiver} - end. - pause_monitor({_Sender, none}) -> ok; pause_monitor({_Sender, Receiver}) -> Receiver ! pause, ok. -- cgit v1.2.1 From 28a077aee8d3728fb94e12d87c86de549fe7cd66 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 11 Oct 2013 17:23:49 +0100 Subject: do not pass the queue collector to amqp_1.0 reader --- src/rabbit_reader.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 47eb603f..e00732fd 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1098,8 +1098,7 @@ pack_for_1_0(#v1{parent = Parent, sock = Sock, recv_len = RecvLen, pending_recv = PendingRecv, - queue_collector = QueueCollector, helper_sup = SupPid, buf = Buf, buf_len = BufLen}) -> - {Parent, Sock, RecvLen, PendingRecv, QueueCollector, SupPid, Buf, BufLen}. + {Parent, Sock, RecvLen, PendingRecv, SupPid, Buf, BufLen}. -- cgit v1.2.1 From 8a167cc42cf7ad9ba8cac09668ccb79eed484227 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 14 Oct 2013 13:49:36 +0100 Subject: Consistency --- src/rabbit_connection_sup.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index ebdab666..9ed5dc77 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -47,15 +47,15 @@ start_link() -> %% the queue collector process, since these must not be siblings of the %% reader due to the potential for deadlock if they are added/restarted %% whilst the supervision tree is shutting down. - {ok, IntermediateSup} = + {ok, HelperSup} = supervisor2:start_child( SupPid, - {channel_sup3, {rabbit_connection_helper_sup, start_link, []}, + {helper_sup, {rabbit_connection_helper_sup, start_link, []}, intrinsic, infinity, supervisor, [rabbit_connection_helper_sup]}), {ok, ReaderPid} = supervisor2:start_child( SupPid, - {reader, {rabbit_reader, start_link, [IntermediateSup]}, + {reader, {rabbit_reader, start_link, [HelperSup]}, intrinsic, ?MAX_WAIT, worker, [rabbit_reader]}), {ok, SupPid, ReaderPid}. -- cgit v1.2.1